Otrzymuję tablicę 512 ^ 3 reprezentującą rozkład temperatury z symulacji (napisanej w języku Fortran). Tablica jest przechowywana w pliku binarnym o rozmiarze około 1/2 GB. Muszę znać minimum, maksimum i średnią tej tablicy, a ponieważ i tak wkrótce będę musiał zrozumieć kod Fortran, postanowiłem spróbować i wymyśliłem następującą bardzo prostą procedurę.
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)
Zajmuje to około 25 sekund na plik na używanym komputerze. Wydało mi się to dość długie, więc poszedłem do przodu i wykonałem następujące czynności w Pythonie:
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)
Oczywiście spodziewałem się, że to będzie szybsze, ale naprawdę mnie zaskoczyło. W identycznych warunkach zajmuje to mniej niż sekundę. Średnia odbiega od tej znalezionej przez moją rutynę Fortran (którą również uruchomiłem z 128-bitowymi liczbami zmiennoprzecinkowymi, więc w jakiś sposób bardziej jej ufam), ale tylko na 7. cyfrze znaczącej.
Jak numpy może być tak szybki? Chodzi mi o to, że musisz spojrzeć na każdy wpis tablicy, aby znaleźć te wartości, prawda? Czy robię coś bardzo głupiego w mojej rutynie Fortran, żeby zajęło to dużo więcej czasu?
EDYTOWAĆ:
Aby odpowiedzieć na pytania w komentarzach:
- Tak, również uruchomiłem procedurę Fortran z 32-bitowymi i 64-bitowymi pływakami, ale nie miało to wpływu na wydajność.
- Użyłem,
iso_fortran_env
który zapewnia 128-bitowe liczby zmiennoprzecinkowe. - Używając 32-bitowych liczb zmiennoprzecinkowych, moja średnia jest jednak nieco wyłączona, więc precyzja jest naprawdę problemem.
- Uruchomiłem obie procedury na różnych plikach w różnej kolejności, więc buforowanie powinno być sprawiedliwe w porównaniu, jak sądzę?
- Właściwie próbowałem otworzyć MP, ale czytać z pliku w różnych pozycjach w tym samym czasie. Po przeczytaniu twoich komentarzy i odpowiedzi brzmi to teraz naprawdę głupio i sprawiło, że rutyna również zajęła znacznie więcej czasu. Mógłbym spróbować operacji na macierzy, ale może to nawet nie będzie konieczne.
- Pliki mają rozmiar 1 / 2G, to była literówka, dzięki.
- Spróbuję teraz implementacji tablicy.
EDYCJA 2:
Zaimplementowałem to, co sugerowali @Alexander Vogt i @casey w ich odpowiedziach i jest to równie szybkie, jak, numpy
ale teraz mam problem z precyzją, jak wskazał @Luaan. Używając 32-bitowej tablicy zmiennoprzecinkowej, średnia obliczona przez sum
to 20% zniżki. Robić
...
real,allocatable :: tmp (:,:,:)
double precision,allocatable :: tmp2(:,:,:)
...
tmp2=tmp
mean=sum(tmp2)/size(tmp)
...
Rozwiązuje problem, ale wydłuża czas przetwarzania (nie bardzo, ale zauważalnie). Czy jest lepszy sposób na obejście tego problemu? Nie mogłem znaleźć sposobu na odczytanie singli bezpośrednio z pliku do podwójnych. Jak tego numpy
uniknąć?
Dziękuję za całą dotychczasową pomoc.