为什么我的 Fortran 代码用 f2py 包装使用了这么多内存?
Why is my Fortran code wrapped with f2py using so much memory?
我正在尝试计算大约十万个点之间的所有距离。我有以下代码用 Fortran 编写并使用 f2py
:
编译
C 1 2 3 4 5 6 7
C123456789012345678901234567890123456789012345678901234567890123456789012
subroutine distances(coor,dist,n)
double precision coor(n,3),dist(n,n)
integer n
double precision x1,y1,z1,x2,y2,z2,diff2
cf2py intent(in) :: coor,dist
cf2py intent(in,out):: dist
cf2py intent(hide)::n
cf2py intent(hide)::x1,y1,z1,x2,y2,z2,diff2
do 200,i=1,n-1
x1=coor(i,1)
y1=coor(i,2)
z1=coor(i,3)
do 100,j=i+1,n
x2=coor(j,1)
y2=coor(j,2)
z2=coor(j,3)
diff2=(x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)+(z1-z2)*(z1-z2)
dist(i,j)=sqrt(diff2)
100 continue
200 continue
end
我正在使用以下 python 代码 setup_collision.py
:
编译 Fortran 代码
# System imports
from distutils.core import *
from distutils import sysconfig
# Third-party modules
import numpy
from numpy.distutils.core import Extension, setup
# Obtain the numpy include directory. This logic works across numpy versions.
try:
numpy_include = numpy.get_include()
except AttributeError:
numpy_include = numpy.get_numpy_include()
# simple extension module
collision = Extension(name="collision",sources=['./collision.f'],
include_dirs = [numpy_include],
)
# NumyTypemapTests setup
setup( name = "COLLISION",
description = "Module calculates collision energies",
author = "Stvn66",
version = "0.1",
ext_modules = [collision]
)
然后运行如下:
import numpy as np
import collision
coor = np.loadtxt('coordinates.txt')
n_atoms = len(coor)
dist = np.zeros((n_atoms, n_atoms), dtype=np.float16) # float16 reduces memory
n_dist = n_atoms*(n_atoms-1)/2
n_GB = n_dist * 2 / float(2**30) # 1 kB = 1024 B
n_Gb = n_dist * 2 / 1E9 # 1 kB = 1000 B
print 'calculating %d distances between %d atoms' % (n_dist, n_atoms)
print 'should use between %f and %f GB of memory' % (n_GB, n_Gb)
dist = collision.distances(coor, dist)
使用这个包含 30,000 个原子的代码,应该使用大约 1 GB 的内存来存储距离,它却使用了 10 GB。与此不同,使用 100,000 个原子执行此计算将需要 100 GB 而不是 10 GB。我的电脑只有 20 GB。
我是否遗漏了与在 Python 和 Fortran 之间传递数据相关的内容?巨大的差异表明实施存在重大缺陷。
您正在将双精度数组提供给 Fortran 子例程。双精度的每个元素需要 8 字节的内存。对于 N=30,000
这使得
coor(n,3) => 30,000*3*8 ~ 0.7 MB
dist(n,n) => 30,000^2*8 ~ 6.7 GB
由于 Python 还需要半精度浮点数,这又占了 1-2GB。所以总体要求是9-10GB。
N=100,000
也是如此,仅 Fortran 部分就需要约 75GB。
而不是 double precision
浮点数,您应该使用单精度 real
s - 如果这足以满足您的计算需求。这将导致一半的内存需求。 [我没有这方面的经验,但我假设如果两个部分使用相同的精度,Python 可以直接对数据进行操作...]
正如@VladimirF 在他的评论中指出的那样,"usual compilers do not support 2 byte reals"。我检查了 gfortran
和 ifort
,他们都没有。所以你至少需要使用单精度。
我正在尝试计算大约十万个点之间的所有距离。我有以下代码用 Fortran 编写并使用 f2py
:
C 1 2 3 4 5 6 7
C123456789012345678901234567890123456789012345678901234567890123456789012
subroutine distances(coor,dist,n)
double precision coor(n,3),dist(n,n)
integer n
double precision x1,y1,z1,x2,y2,z2,diff2
cf2py intent(in) :: coor,dist
cf2py intent(in,out):: dist
cf2py intent(hide)::n
cf2py intent(hide)::x1,y1,z1,x2,y2,z2,diff2
do 200,i=1,n-1
x1=coor(i,1)
y1=coor(i,2)
z1=coor(i,3)
do 100,j=i+1,n
x2=coor(j,1)
y2=coor(j,2)
z2=coor(j,3)
diff2=(x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)+(z1-z2)*(z1-z2)
dist(i,j)=sqrt(diff2)
100 continue
200 continue
end
我正在使用以下 python 代码 setup_collision.py
:
# System imports
from distutils.core import *
from distutils import sysconfig
# Third-party modules
import numpy
from numpy.distutils.core import Extension, setup
# Obtain the numpy include directory. This logic works across numpy versions.
try:
numpy_include = numpy.get_include()
except AttributeError:
numpy_include = numpy.get_numpy_include()
# simple extension module
collision = Extension(name="collision",sources=['./collision.f'],
include_dirs = [numpy_include],
)
# NumyTypemapTests setup
setup( name = "COLLISION",
description = "Module calculates collision energies",
author = "Stvn66",
version = "0.1",
ext_modules = [collision]
)
然后运行如下:
import numpy as np
import collision
coor = np.loadtxt('coordinates.txt')
n_atoms = len(coor)
dist = np.zeros((n_atoms, n_atoms), dtype=np.float16) # float16 reduces memory
n_dist = n_atoms*(n_atoms-1)/2
n_GB = n_dist * 2 / float(2**30) # 1 kB = 1024 B
n_Gb = n_dist * 2 / 1E9 # 1 kB = 1000 B
print 'calculating %d distances between %d atoms' % (n_dist, n_atoms)
print 'should use between %f and %f GB of memory' % (n_GB, n_Gb)
dist = collision.distances(coor, dist)
使用这个包含 30,000 个原子的代码,应该使用大约 1 GB 的内存来存储距离,它却使用了 10 GB。与此不同,使用 100,000 个原子执行此计算将需要 100 GB 而不是 10 GB。我的电脑只有 20 GB。
我是否遗漏了与在 Python 和 Fortran 之间传递数据相关的内容?巨大的差异表明实施存在重大缺陷。
您正在将双精度数组提供给 Fortran 子例程。双精度的每个元素需要 8 字节的内存。对于 N=30,000
这使得
coor(n,3) => 30,000*3*8 ~ 0.7 MB
dist(n,n) => 30,000^2*8 ~ 6.7 GB
由于 Python 还需要半精度浮点数,这又占了 1-2GB。所以总体要求是9-10GB。
N=100,000
也是如此,仅 Fortran 部分就需要约 75GB。
而不是 double precision
浮点数,您应该使用单精度 real
s - 如果这足以满足您的计算需求。这将导致一半的内存需求。 [我没有这方面的经验,但我假设如果两个部分使用相同的精度,Python 可以直接对数据进行操作...]
正如@VladimirF 在他的评论中指出的那样,"usual compilers do not support 2 byte reals"。我检查了 gfortran
和 ifort
,他们都没有。所以你至少需要使用单精度。