Я получаю массив 512 ^ 3, представляющий распределение температуры от моделирования (написанное на языке Fortran). Массив хранится в двоичном файле размером около 1/2G. Мне нужно знать минимальный, максимальный и средний из этого массива, и поскольку мне скоро понадобится понять код Fortran, я решил дать ему повод и придумал следующую очень легкую процедуру.
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)
Это занимает около 25 секунд на файл на машине, которую я использую. Это показалось мне довольно длинным, поэтому я пошел дальше и сделал следующее на 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)
Теперь я ожидал, что это будет быстрее, но я действительно сдулся. В то же время требуется меньше секунды. Среднее отклоняется от одного моего подпрограммы "Fortran" (который я также запускал с 128-битными поплавками, поэтому я как-то доверяю ему больше), но только на 7-й значащей цифре или около того.
Как numpy может быть настолько быстрым? Я имею в виду, что вы должны смотреть на каждую запись массива, чтобы найти эти значения, не так ли? Я делаю что-то очень глупо в моей программе Fortran, чтобы она занимала намного больше времени?
EDIT:
Чтобы ответить на вопросы в комментариях:
- Да, я также запускал подпрограмму Fortran с 32-битными и 64-битными поплавками, но не влиял на производительность.
- Я использовал
iso_fortran_env
, который обеспечивает 128-битные поплавки. - Используя 32-битные поплавки, мое среднее значение довольно велико, поэтому точность действительно является проблемой.
- Я запускал обе процедуры в разных файлах в другом порядке, поэтому кеширование должно быть справедливым в сравнении, я думаю?
- Я на самом деле пробовал открытый MP, но читал из файла на разных позициях одновременно. Прочитав ваши комментарии и ответы, это звучит действительно глупо сейчас, и это заставило процедуру заняться намного дольше. Я мог бы дать ему попробовать массивные операции, но, возможно, это даже не понадобится.
- Файлы на самом деле размером 1/2G, это была опечатка, спасибо.
- Теперь я попробую реализацию массива.
ИЗМЕНИТЬ 2:
Я реализовал то, что @Alexander Vogt и @casey предложили в своих ответах, и это так же быстро, как numpy
, но теперь у меня есть проблема с точностью, которую, как заметил @Luaan, я могу получить. Используя 32-битный массив с плавающей точкой, среднее значение, вычисленное на sum
, составляет 20%. Выполнение
...
real,allocatable :: tmp (:,:,:)
double precision,allocatable :: tmp2(:,:,:)
...
tmp2=tmp
mean=sum(tmp2)/size(tmp)
...
Решает проблему, но увеличивает вычислительное время (не очень, но заметно).
Есть ли лучший способ обойти эту проблему? Я не мог найти способ прочитать синглы из файла непосредственно в парном разряде.
И как numpy
избежать этого?
Спасибо за всю помощь до сих пор.