Ich erhalte ein 512 ^ 3-Array, das eine Temperaturverteilung aus einer Simulation darstellt (geschrieben in Fortran). Das Array wird in einer Binärdatei mit einer Größe von etwa 1 / 2G gespeichert. Ich muss das Minimum, Maximum und den Mittelwert dieses Arrays kennen und da ich den Fortran-Code sowieso bald verstehen muss, habe ich beschlossen, es auszuprobieren, und mir die folgende sehr einfache Routine ausgedacht.
integer gridsize,unit,j
real mini,maxi
double precision mean
gridsize=512
unit=40
open(unit=unit,file='T.out',status='old',access='stream',&
form='unformatted',action='read')
read(unit=unit) tmp
mini=tmp
maxi=tmp
mean=tmp
do j=2,gridsize**3
read(unit=unit) tmp
if(tmp>maxi)then
maxi=tmp
elseif(tmp<mini)then
mini=tmp
end if
mean=mean+tmp
end do
mean=mean/gridsize**3
close(unit=unit)
Dies dauert ungefähr 25 Sekunden pro Datei auf dem von mir verwendeten Computer. Das kam mir ziemlich lang vor und so machte ich in Python Folgendes:
import numpy
mmap=numpy.memmap('T.out',dtype='float32',mode='r',offset=4,\
shape=(512,512,512),order='F')
mini=numpy.amin(mmap)
maxi=numpy.amax(mmap)
mean=numpy.mean(mmap)
Nun, ich hatte erwartet, dass dies natürlich schneller sein würde, aber ich war wirklich überwältigt. Unter identischen Bedingungen dauert es weniger als eine Sekunde. Der Mittelwert weicht von dem ab, den meine Fortran-Routine findet (den ich auch mit 128-Bit-Floats ausgeführt habe, also vertraue ich ihm irgendwie mehr), aber nur auf der 7. signifikanten Ziffer oder so.
Wie kann Numpy so schnell sein? Ich meine, Sie müssen sich jeden Eintrag eines Arrays ansehen, um diese Werte zu finden, oder? Mache ich in meiner Fortran-Routine etwas sehr Dummes, damit es so viel länger dauert?
BEARBEITEN:
So beantworten Sie die Fragen in den Kommentaren:
- Ja, ich habe auch die Fortran-Routine mit 32-Bit- und 64-Bit-Floats ausgeführt, aber sie hatte keinen Einfluss auf die Leistung.
- Ich habe verwendet,
iso_fortran_env
die 128-Bit-Floats bietet. - Bei Verwendung von 32-Bit-Floats ist mein Mittelwert jedoch ziemlich niedrig, sodass Präzision wirklich ein Problem darstellt.
- Ich habe beide Routinen auf verschiedenen Dateien in unterschiedlicher Reihenfolge ausgeführt, also sollte das Caching im Vergleich fair sein, denke ich?
- Ich habe tatsächlich versucht, MP zu öffnen, aber gleichzeitig an verschiedenen Positionen aus der Datei zu lesen. Nachdem Sie Ihre Kommentare und Antworten gelesen haben, klingt dies jetzt wirklich dumm und die Routine hat auch viel länger gedauert. Ich könnte es mit den Array-Operationen versuchen, aber vielleicht ist das gar nicht nötig.
- Die Dateien sind tatsächlich 1 / 2G groß, das war ein Tippfehler, danke.
- Ich werde jetzt die Array-Implementierung versuchen.
EDIT 2:
Ich habe implementiert, was @Alexander Vogt und @casey in ihren Antworten vorgeschlagen haben, und es ist so schnell wie, numpy
aber jetzt habe ich ein Präzisionsproblem, wie @Luaan darauf hingewiesen hat, dass ich es bekommen könnte. Bei Verwendung eines 32-Bit-Float-Arrays sum
beträgt der berechnete Mittelwert 20%. Tun
...
real,allocatable :: tmp (:,:,:)
double precision,allocatable :: tmp2(:,:,:)
...
tmp2=tmp
mean=sum(tmp2)/size(tmp)
...
Behebt das Problem, erhöht aber die Rechenzeit (nicht sehr stark, aber spürbar). Gibt es einen besseren Weg, um dieses Problem zu umgehen? Ich konnte keinen Weg finden, Singles aus der Datei direkt in Doppel zu lesen. Und wie numpy
vermeidet man das?
Vielen Dank für die bisherige Hilfe.