我从仿真中得到了一个代表温度分布的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例程中做了一些非常愚蠢的事情,以使其花费了更长的时间?
编辑:
要回答评论中的问题:
- 是的,我也使用32位和64位浮点数运行了Fortran例程,但它对性能没有影响。
- 我使用
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
避免这种情况?
感谢到目前为止的所有帮助。