python - ¿Por qué mi código Fortran está envuelto con f2py usando tanta memoria?
memory-management out-of-memory (1)
Estoy tratando de calcular todas las distancias entre aproximadamente cien mil puntos. Tengo el siguiente código escrito en Fortran y compilado usando 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
Estoy compilando el código setup_collision.py
usando el siguiente código 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]
)
Luego ejecutarlo de la siguiente manera:
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)
Usando este código con 30,000 átomos, lo que debería usar alrededor de 1 GB de memoria para almacenar las distancias, en su lugar usa 10 GB. Con esta diferencia, realizar este cálculo con 100.000 átomos requerirá 100 GB en lugar de 10 GB. Solo tengo 20 GB en mi computadora.
¿Me estoy perdiendo algo relacionado con pasar los datos entre Python y Fortran? La gran diferencia indica un defecto importante en la implementación.
Está alimentando matrices de doble precisión a la subrutina Fortran. Cada elemento con doble precisión requiere 8 bytes de memoria. Para N=30,000
eso hace
coor(n,3) => 30,000*3*8 ~ 0.7 MB
dist(n,n) => 30,000^2*8 ~ 6.7 GB
Dado que los flotantes de media precisión son necesarios para Python, eso representa otro 1-2GB. Entonces el requisito general es 9-10GB.
Lo mismo es válido para N=100,000
, que requerirá ~ 75GB para la parte de Fortran solo.
En lugar de flotadores de double precision
, debe usar valores real
precisión simple, si eso es suficiente para sus cálculos. Esto llevará a la mitad de los requisitos de memoria. [No tengo experiencia con eso, pero supongo que si ambas partes usan la misma precisión, Python puede operar los datos directamente ...]
Como @VladimirF señaló en su comentario, "los compiladores habituales no admiten reales de 2 bytes" . Comprobé con gfortran
e ifort
, y los dos no. Entonces necesitas usar al menos una sola precisión.