如何释放由与 Cython 模块接口的外部 C 库分配的内存,其中内存最终返回到 Python 进程?
How to free memory allocated by external C libraries interfacing with Cython module where the memory is ultimately returned to a Python process?
我是 Cython 的新手,但基本上我的这个应用程序需要显着提高性能,所以我和我的团队正在尝试重写我们在 Cython 和 C 中的瓶颈。
对于我们应用程序中最慢的部分,我编写了一些 C 代码,这些代码被编译到一个库中并 cdef extern
导入到一个 Cython 模块中,我认为它是一个 .pyx
文件。本质上,pyx
文件中的代码基本上只是 returns 调用 C 库函数的包装器。最后,有一个 Python 进程(主应用程序)导入 pyx
文件中定义的所有函数并使用这些结果。
我认为我有内存泄漏,因为在 C 代码中,我需要传递给 Python 进程的结果有时是动态分配的。我的问题是,一旦 Python 进程使用了它,我不知道如何释放它。
示例Python代码
from examplecython import *
def foo(data):
context = data.context
value = call_pyx_function(context, data)
return value
def bar(results):
for data in results:
res = foo(data)
do_something_with_res(res)
# I want to free here
示例 Cython 代码
cdef extern from "my_lib.h"
char * my_function(const char * context, int data)
def call_pyx_function(context: bytes, int x):
return my_function(context, x)
示例 C 代码
#define BUFSIZE 256
char *
my_function(const char * context, int x) {
char * retbuf;
int res;
retbuf = (char *)malloc(BUFSIZE * sizeof(char));
res = do_some_math(x, context);
int length = snprintf(retbuf, BUFSIZE, "%d", res);
if (length >= BUFSIZE) {
exit(EXIT_FAILURE);
}
return retbuf;
}
如果有人对我可以释放此内存的方式和位置有任何建议,将不胜感激。
您可以直接从 libc.stdlib
导入 free
:
from libc.stdlib cimport free
def bar(results):
for data in results:
res = foo(data)
try:
do_something_with_res(res)
finally:
free(res)
(请注意,您需要 try/finally
,因为您希望即使出现异常也能释放它)
您可以使用上下文管理器或在 __del__
/ __dealloc__
:
中删除的包装器使这更容易
@contextlib.contextmanager
def freeing(res):
try:
yield res
finally:
free(res)
def bar(results):
for data in results:
with freeing(foo(data)) as res:
do_something_with_res(res)
或者(可能会在更晚的时候被释放,可能会更慢,但(几乎)保证最终会被释放)
# (in pyx file)
cdef class MallocedResource:
cdef void* res;
def __init__(self, res):
# Note: This "steals" res. Don't free `res`
# as it is freed when this class's storage is freed
self.res = <void *>res
def __dealloc__(self):
free(self.res)
def call_pyx_function(context: bytes, int x):
return MallocedResouce(my_function(context, x))
# No need to change python code, so you can't forget to use try/finally.
我是 Cython 的新手,但基本上我的这个应用程序需要显着提高性能,所以我和我的团队正在尝试重写我们在 Cython 和 C 中的瓶颈。
对于我们应用程序中最慢的部分,我编写了一些 C 代码,这些代码被编译到一个库中并 cdef extern
导入到一个 Cython 模块中,我认为它是一个 .pyx
文件。本质上,pyx
文件中的代码基本上只是 returns 调用 C 库函数的包装器。最后,有一个 Python 进程(主应用程序)导入 pyx
文件中定义的所有函数并使用这些结果。
我认为我有内存泄漏,因为在 C 代码中,我需要传递给 Python 进程的结果有时是动态分配的。我的问题是,一旦 Python 进程使用了它,我不知道如何释放它。
示例Python代码
from examplecython import *
def foo(data):
context = data.context
value = call_pyx_function(context, data)
return value
def bar(results):
for data in results:
res = foo(data)
do_something_with_res(res)
# I want to free here
示例 Cython 代码
cdef extern from "my_lib.h"
char * my_function(const char * context, int data)
def call_pyx_function(context: bytes, int x):
return my_function(context, x)
示例 C 代码
#define BUFSIZE 256
char *
my_function(const char * context, int x) {
char * retbuf;
int res;
retbuf = (char *)malloc(BUFSIZE * sizeof(char));
res = do_some_math(x, context);
int length = snprintf(retbuf, BUFSIZE, "%d", res);
if (length >= BUFSIZE) {
exit(EXIT_FAILURE);
}
return retbuf;
}
如果有人对我可以释放此内存的方式和位置有任何建议,将不胜感激。
您可以直接从 libc.stdlib
导入 free
:
from libc.stdlib cimport free
def bar(results):
for data in results:
res = foo(data)
try:
do_something_with_res(res)
finally:
free(res)
(请注意,您需要 try/finally
,因为您希望即使出现异常也能释放它)
您可以使用上下文管理器或在 __del__
/ __dealloc__
:
@contextlib.contextmanager
def freeing(res):
try:
yield res
finally:
free(res)
def bar(results):
for data in results:
with freeing(foo(data)) as res:
do_something_with_res(res)
或者(可能会在更晚的时候被释放,可能会更慢,但(几乎)保证最终会被释放)
# (in pyx file)
cdef class MallocedResource:
cdef void* res;
def __init__(self, res):
# Note: This "steals" res. Don't free `res`
# as it is freed when this class's storage is freed
self.res = <void *>res
def __dealloc__(self):
free(self.res)
def call_pyx_function(context: bytes, int x):
return MallocedResouce(my_function(context, x))
# No need to change python code, so you can't forget to use try/finally.