Obtengo una matriz de 512 ^ 3 que representa una distribución de temperatura de una simulación (escrita en Fortran). La matriz se almacena en un archivo binario de aproximadamente 1 / 2G de tamaño. Necesito saber el mínimo, el máximo y la media de esta matriz y, como pronto necesitaré comprender el código de Fortran de todos modos, decidí intentarlo y se me ocurrió la siguiente rutina muy fácil.
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)
Esto lleva unos 25 segundos por archivo en la máquina que utilizo. Eso me pareció bastante largo, así que seguí adelante e hice lo siguiente en Python:
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)
Ahora, esperaba que esto fuera más rápido, por supuesto, pero estaba realmente impresionado. Tarda menos de un segundo en condiciones idénticas. La media se desvía de la que encuentra mi rutina de Fortran (que también ejecuté con flotantes de 128 bits, así que de alguna manera confío más en ella) pero solo en el séptimo dígito significativo más o menos.
¿Cómo puede ser tan rápido Numpy? Quiero decir, tienes que mirar cada entrada de una matriz para encontrar estos valores, ¿verdad? ¿Estoy haciendo algo muy estúpido en mi rutina de Fortran para que tarde tanto tiempo?
EDITAR:
Para responder a las preguntas en los comentarios:
- Sí, también ejecuté la rutina Fortran con flotadores de 32 y 64 bits, pero no tuvo ningún impacto en el rendimiento.
- Usé
iso_fortran_env
que proporciona flotantes de 128 bits. - Sin embargo, al usar flotadores de 32 bits, mi promedio está bastante desviado, por lo que la precisión es realmente un problema.
- Ejecuté ambas rutinas en archivos diferentes en un orden diferente, por lo que el almacenamiento en caché debería haber sido justo en la comparación, supongo.
- De hecho, intenté abrir MP, pero para leer el archivo en diferentes posiciones al mismo tiempo. Habiendo leído sus comentarios y respuestas, esto suena realmente estúpido ahora y también hizo que la rutina tomara mucho más tiempo. Podría intentarlo en las operaciones de la matriz, pero tal vez ni siquiera sea necesario.
- Los archivos tienen un tamaño de 1 / 2G, eso fue un error tipográfico, gracias.
- Probaré la implementación de la matriz ahora.
EDITAR 2:
Implementé lo que @Alexander Vogt y @casey sugirieron en sus respuestas, y es tan rápido como, numpy
pero ahora tengo un problema de precisión como @Luaan señaló que podría tener. Usando una matriz flotante de 32 bits, la media calculada por sum
es un 20% de descuento. Haciendo
...
real,allocatable :: tmp (:,:,:)
double precision,allocatable :: tmp2(:,:,:)
...
tmp2=tmp
mean=sum(tmp2)/size(tmp)
...
Resuelve el problema pero aumenta el tiempo de cálculo (no mucho, pero sí de forma notable). ¿Existe una mejor manera de solucionar este problema? No pude encontrar una manera de leer sencillos del archivo directamente en dobles. ¿Y cómo numpy
evita esto?
Gracias por toda la ayuda hasta ahora.