シミュレーション(Fortranで記述)から温度分布を表す512 ^ 3配列を取得します。配列は、サイズが約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)
さて、もちろんこれはもっと速いと思っていましたが、本当に感動しました。同じ条件下で1秒もかかりません。平均は、私のFortranルーチンが見つけたもの(私も128ビットのfloatで実行したので、どういうわけかそれをもっと信頼しています)から外れていますが、有効数字の7桁程度にすぎません。
どうしてnumpyはこんなに速くなるのでしょうか?つまり、これらの値を見つけるには、配列のすべてのエントリを調べる必要がありますよね?私はFortranルーチンで非常に愚かなことをしているので、もっと時間がかかりますか?
編集:
コメントの質問に答えるには:
- はい、32ビットと64ビットのfloatを使用してFortranルーチンを実行しましたが、パフォーマンスに影響はありませんでした。
iso_fortran_env
128ビットのフロートを提供するものを使用しました。- 32ビットフロートを使用すると、私の平均はかなりずれているので、精度が本当に問題になります。
- 私は両方のルーチンを異なるファイルで異なる順序で実行したので、キャッシュは私が推測する比較では公平であるはずでしたか?
- 私は実際にMPを開いてみましたが、同時に異なる位置でファイルから読み取りました。あなたのコメントと答えを読んだので、これは今本当にばかげているように聞こえます、そしてそれはルーチンもずっと長くかかるようにしました。配列操作を試してみるかもしれませんが、それは必要ないかもしれません。
- ファイルのサイズは実際には1 / 2Gですが、これはタイプミスでした。ありがとうございます。
- 配列の実装を試してみます。
編集2:
@Alexander Vogtと@caseyが回答で提案したものを実装しました。それは同じくらい高速numpy
ですが、@ Luaanが指摘したように、精度の問題が発生します。32ビットのfloat配列を使用すると、によって計算される平均sum
は20%オフになります。やってる
...
real,allocatable :: tmp (:,:,:)
double precision,allocatable :: tmp2(:,:,:)
...
tmp2=tmp
mean=sum(tmp2)/size(tmp)
...
問題を解決しますが、計算時間が長くなります(それほどではありませんが、著しく)。この問題を回避するためのより良い方法はありますか?ファイルからシングルを直接ダブルに読み取る方法が見つかりませんでした。そして、どうすればnumpy
これを回避できますか?
これまでのすべての助けに感謝します。