Tengo una función C que mallocs() y rellena una matriz 2D de flotantes. "Devuelve" esa dirección y el tamaño de la matriz. La firma es¿Puedo forzar a un ndarray numpy a tomar posesión de su memoria?
int get_array_c(float** addr, int* nrows, int* ncols);
Quiero llamarlo desde Python, entonces uso ctypes.
import ctypes
mylib = ctypes.cdll.LoadLibrary('mylib.so')
get_array_c = mylib.get_array_c
Nunca he averiguado cómo especificar tipos de argumentos con ctypes. Tiendo a escribir simplemente un contenedor de Python para cada función de C que estoy usando, y me aseguro de obtener los tipos en el contenedor. La matriz de flotadores es una matriz en orden de columna principal, y me gustaría obtenerla como numpy.ndarray. Pero es bastante grande, así que quiero usar la memoria asignada por la función C, no copiarla. (Acabo de encontrar estas cosas PyBuffer_FromMemory en esta respuesta StackOverflow: https://stackoverflow.com/a/4355701/3691)
buffer_from_memory = ctypes.pythonapi.PyBuffer_FromMemory
buffer_from_memory.restype = ctypes.py_object
import numpy
def get_array_py():
nrows = ctypes.c_int()
ncols = ctypes.c_int()
addr_ptr = ctypes.POINTER(ctypes.c_float)()
get_array_c(ctypes.byref(addr_ptr), ctypes.byref(nrows), ctypes.byref(ncols))
buf = buffer_from_memory(addr_ptr, 4 * nrows * ncols)
return numpy.ndarray((nrows, ncols), dtype=numpy.float32, order='F',
buffer=buf)
Esto me parece dar una matriz con los valores correctos. Pero estoy bastante seguro de que es una pérdida de memoria.
>>> a = get_array_py()
>>> a.flags.owndata
False
La matriz no posee la memoria. Lo suficientemente justo; de forma predeterminada, cuando la matriz se crea desde un búfer, no debería. Pero en este caso debería. Cuando se elimina la matriz numpy, realmente me gustaría que Python libere la memoria buffer para mí. Parece que si pudiera forzar mis propios datos a True, eso debería hacerlo, pero mis propios datos no son configurables.
soluciones no son satisfactorios:
hacer que la persona que llama de get_array_py() responsable de liberar la memoria. Eso es súper molesto; la persona que llama debería ser capaz de tratar esta matriz numpy como cualquier otra matriz numpy.
Copie la matriz original en una nueva matriz numpy (con su propia memoria separada) en get_array_py, elimine la primera matriz y libere la memoria en get_array_py(). Devuelve la copia en lugar de la matriz original. Esto es molesto porque es una copia de memoria innecesaria.
¿Hay alguna manera de hacer lo que quiero? No puedo modificar la función C en sí, aunque podría agregar otra función C a la biblioteca si eso es útil.
Esto suena como un mundo de dolor .. Creo que estás pidiendo [segfault hell] (http://xkcd.com/371/) – wim
He intentado esto también sin éxito usando ctypes. Un módulo de extensión completo lo hace posible, pero son más trabajos para escribir. –