有没有理由在PyMem_Malloc上使用malloc?
我正在阅读Python C扩展中的内存管理文档,据我所知,似乎没有太多理由使用malloc
而不是PyMem_Malloc
。 假设我想分配一个不会暴露给Python源代码的数组,并将存储在一个将被垃圾回收的对象中。 有没有理由使用malloc
?
编辑 :混合PyMem_Malloc
和PyObject_Malloc
更正; 他们是两个不同的电话。
如果没有激活PYMALLOC_DEBUG
宏, PyMem_Malloc
是libc的malloc()
的别名,有一个特殊情况:调用PyMem_Malloc
分配零字节将返回非NULL指针,而malloc(zero_bytes)可能返回NULL值或引发系统错误( 源代码参考 ):
/ * malloc。 请注意,nbytes == 0尝试返回非NULL指针,与所有其他当前活动指针不同。 这可能是不可能的。 * /
另外, pymem.h
头文件中有一条建议说明:
切勿将对PyMem_的调用与对平台malloc / realloc / calloc / free的调用混合。 例如,在Windows上,不同的DLL最终可能会使用不同的堆,如果使用PyMem_Malloc,您将从Python DLL使用的堆中获取内存; 如果你直接在你自己的扩展中免费()编辑它可能是一场灾难。 使用PyMem_Free可以确保Python可以将内存返回到正确的堆。 另一个例子,在PYMALLOC_DEBUG模式中,Python将所有调用PyMem_和PyObject_内存函数的所有调用包装在特殊的调试包装器中,这些包装器为动态内存块添加了额外的调试信息。 系统例程不知道如何处理这些东西,并且Python包装器不知道如何处理由系统例程直接获得的原始块。
然后,在PyMem_Malloc
PyObject_Malloc
有一些Python特定的调整,这个函数不仅用于C扩展,而且用于运行Python程序时的所有动态分配,如100*234
, str(100)
或10 + 4j
:
>>> id(10 + 4j) 139721697591440 >>> id(10 + 4j) 139721697591504 >>> id(10 + 4j) 139721697591440
以前的complex()
实例是在专用池上分配的小对象。
使用PyMem_Malloc
PyObject_Malloc
分配小对象(<256字节)是非常有效的,因为它是从池中8字节对齐的块完成的,每个块大小存在一个池。 还有用于更大分配的Pages和Arenas块。
这篇关于源代码的评论解释了如何优化PyObject_Malloc
调用:
/* * The basic blocks are ordered by decreasing execution frequency, * which minimizes the number of jumps in the most common cases, * improves branching prediction and instruction scheduling (small * block allocations typically result in a couple of instructions). * Unless the optimizer reorders everything, being too smart... */
Pools,Pages和Arenas是旨在减少长期运行的Python程序的外部内存碎片的优化。
查看有关Python内存内部的完整详细文档的源代码 。
扩展使用malloc或其他系统分配器分配内存是完全可以的。 对于许多类型的模块来说,这是正常的,也是不可避免的 – 大多数包装其他库的模块,它们本身对Python一无所知,当它们在该库中发生时会导致本机分配。 (有些库允许您控制分配足以防止这种情况;大多数库都没有。)
使用PyMem_Malloc有一个严重的缺点:使用它时需要保持GIL。 本机库通常希望在进行CPU密集型计算或进行任何可能阻塞的调用(如I / O)时释放GIL。 需要在分配之前锁定GIL可能介于非常不方便和性能问题之间。
使用Python的包装器进行内存分配允许使用Python的内存调试代码。 有了像Valgrind这样的工具,我怀疑它的真实世界价值。
如果API需要,您将需要使用这些function; 例如,如果API传递了必须使用这些函数分配的指针,那么可以使用它们释放它。 除非有明确的理由使用它们,否则我坚持正常分配。
根据我编写MATLAB .mex函数的经验,我认为是否使用malloc的最大决定因素是可移植性。 假设你有一个头文件,只使用内部c数据类型执行大量有用的function(没有必要的Python对象交互,所以使用malloc没问题),你突然意识到你想要将该头文件移植到另一个代码库中与Python无关(也许这是一个纯粹用C语言编写的项目),使用malloc显然是一个更加便携的解决方案。
但是对于纯粹是Python扩展的代码,我最初的反应是期望本机c函数执行得更快。 我没有证据支持这个:)