【发布时间】:2016-02-16 20:47:20
【问题描述】:
我得到一个 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 例程中做了一些非常愚蠢的事情,以至于需要更长的时间?
编辑:
回答cmets中的问题:
- 是的,我还使用 32 位和 64 位浮点数运行了 Fortran 例程,但它对性能没有影响。
- 我使用了
iso_fortran_env,它提供了 128 位浮点数。 - 使用 32 位浮点数虽然我的意思有点偏离,所以精度确实是个问题。
- 我以不同的顺序在不同的文件上运行了这两个例程,所以我猜在比较中缓存应该是公平的?
- 我实际上尝试过打开MP,但要同时从不同位置的文件中读取。阅读了您的 cmets 并回答了这听起来现在真的很愚蠢,而且这也使例行程序花费了更长的时间。我可能会尝试一下数组操作,但也许这甚至没有必要。
- 文件大小实际上是 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 又是如何避免这种情况的呢?
感谢到目前为止的所有帮助。
【问题讨论】:
-
您是否尝试过没有 128 位浮点数的 Fortran 例程?我不知道有任何硬件实际上支持这些,所以它们必须在软件中完成。
-
如果您尝试使用数组的 Fortran 版本(特别是使用一次读取而不是十亿次读取)会怎样?
-
您是否考虑过在 Fortran 中也使用数组运算符?然后,您可以尝试
minval()、maxval()和sum()?此外,您将 IO 与 Fortran 中的操作混合在一起,而不是在 Python 中 - 这不是一个公平的比较;-) -
在对涉及大文件的内容进行基准测试时,请确保所有运行的缓存都相同。
-
另请注意,精度在 Fortran 中是一个相当大的问题,而且它是有代价的。即使你用你的 Fortran 代码解决了所有这些明显的问题,也很可能需要额外的精度,并且会导致显着的速度损失。
标签: python arrays performance numpy fortran