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