9

我正在阅读Python C extensions 中的内存管理文档,据我所知,似乎没有太多理由使用malloc而不是PyMem_Malloc. 假设我想分配一个不会暴露给 Python 源代码并将存储在将被垃圾收集的对象中的数组。有什么理由使用malloc吗?

4

3 回答 3

8

编辑:混合PyMem_MallocPyObject_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 包装器也不知道如何处理系统例程直接获得的原始块。

然后,里面有一些 Python 特定的调优,这个函数不仅用于 C 扩展,还用于运行 Python 程序时的所有动态分配,例如,或:PyMem_Malloc PyObject_Malloc100*234str(100)10 + 4j

>>> id(10 + 4j)
139721697591440
>>> id(10 + 4j)
139721697591504
>>> id(10 + 4j)
139721697591440

前面的complex()实例是分配在专用池上的小对象。

小对象(<256 字节)分配非常有效,因为它是从一个 8 字节对齐的块池中完成的,每个块大小都存在一个池。还有用于更大分配的 Pages 和 Arenas 块。PyMem_Malloc PyObject_Malloc

此对源代码的注释解释了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 内存内部结构的完整详细文档。

于 2011-01-28T01:30:31.120 回答
7

扩展使用 malloc 或其他系统分配器分配内存是完全可以的。对于许多类型的模块来说,这是正常且不可避免的——大多数包装了其他库的模块,这些库本身对 Python 一无所知,当它们在该库中发生时会导致本地分配。(一些库允许您控制分配以防止这种情况发生;大多数库不允许。)

使用 PyMem_Malloc 有一个严重的缺点:使用时需要持有 GIL。在进行 CPU 密集型计算或进行任何可能阻塞的调用(如 I/O)时,本机库通常希望释放 GIL。在分配之前需要锁定 GIL 可能介于非常不方便和性能问题之间。

使用 Python 的包装器进行内存分配允许使用 Python 的内存调试代码。然而,对于 Valgrind 之类的工具,我怀疑它在现实世界中的价值。

如果 API 需要,您将需要使用这些函数;例如,如果向 API 传递了一个必须分配给这些函数的指针,那么它就可以被它们释放。除非有使用它们的明确原因,否则我坚持正常分配。

于 2011-01-28T01:18:17.533 回答
2

根据我编写 MATLAB .mex 函数的经验,我认为是否使用 malloc 的最大决定因素是可移植性。假设您有一个仅使用内部 c 数据类型执行大量有用功能的头文件(没有必要的 Python 对象交互,因此使用 malloc 没有问题),您突然意识到您想将该头文件移植到具有与 Python 无关(也许它是一个纯粹用 C 编写的项目),使用 malloc 显然是一个更便携的解决方案。

但是对于纯粹是 Python 扩展的代码,我最初的反应是期望原生 c 函数执行得更快。我没有证据支持这一点:)

于 2011-01-27T23:24:06.337 回答