4

我需要用 C 语言编写一个 Python 扩展,用于:

  1. 对文件执行 CPU 密集型初始化;
  2. 进行多个依赖于初始化数据的函数调用以将结果返回给我;和
  3. 完成后释放内存

一种解决方案是在 Python 中实现“状态持有者”类。当我在 C 中调用初始化时,它会返回我将存储在 Python 状态对象中的初始化数据。然后每次我需要执行第 (2) 步时,我都会将其传递给 C 函数。但这似乎非常低效,因为 Python 端和 C 端之间发生的所有数据穿梭/接口。

如果可能的话,我想使用 C 端的状态对象来维护状态。Python 端的初始化调用不会返回所有初始化数据,而只会返回一个 ID,以便在后续调用期间需要时引用 C 状态对象。

我将如何维护 C 端的状态?

4

2 回答 2

4

首先,我将回答您实际提出的问题。

在 C 中创建一个struct State,就像不涉及 Python 一样。

如果你不打算复制这些(你只通过它们struct State *),那么你可以只(intptr_t)theStatePtr为 Python 获取一个 id。当然,您确实需要注意 Python 对象的生命周期永远不会超过 C 对象的生命周期,但这是可行的。

如果您出于某种原因确实需要复制/移动结构,或者您需要更多帮助来管理状态(例如,将 Python id 视为弱引用),请选择适当的集合(哈希表、树、数组等)您的用例,然后将密钥作为 id 传递给 Python。

但是,我认为您可能在这里优化了错误的部分。来回传递对象没什么——它只是一个指针副本。重新计数可能是一个问题,但很少发生,而且您从生命周期管理中获得的好处通常是值得的。可能会影响性能的部分是您的 C 代码不断将一堆 Python 整数转换为 Cint等。如果这是您的问题,只需创建一个具有 C 状态的 C 结构,并将其包装在一个不支持的 Python 对象中将任何内部结构暴露到 Python 中。

最后,您真的需要在这里进行任何优化吗?如果您正在做 CPU 密集型工作,我敢打赌,真正的工作完全掩盖了 Python 对象访问的成本,后者甚至不会出现在分析中。如果您还没有进行分析,那绝对是您应该做的第一件事,因为这里的正确答案很可能是“不要费心做任何事情”。

更进一步:如果您只是在 C 中编写 C 代码以进行优化,您确定还需要吗?在 C 中处理内存管理是烦人且容易出错的,在 Python 的 C 扩展模块中处理它更是如此,当你不知道它是如何工作的时第一次这样做几乎是保证支出的秘诀你所有的时间都在追踪段错误和泄漏,而不是编写你的实际代码。因此,我将按顺序尝试以下操作,对每个进行分析,并且仅在太慢时才将列表向下移动:

  • 只需用 Python 编写算法,并使用现有的 CPython 解释器。
  • 确保你有一个最佳算法。
  • 尝试 PyPy 而不是 CPython。
  • 获取Cython并尝试使用尽可能少的更改来编译您的 Python 代码。
  • 根据需要修改您的代码以利用 Cython 功能,例如静态类型、直接调用 C 函数等。
  • 用 C 语言编写低级代码,用 Cython 或使用ctypes.
  • 使用您最喜欢的接口机制,用 C 语言编写整个低级和中级。这可能仍然不是本机 C API,除非您有很多经验并且正在做一些非常简单的事情。
于 2012-11-07T22:36:34.317 回答
0

查看Cython以轻松实现 python 到 C 的桥接。那里的文档有很多示例——我链接到一个页面,该页面解释了如何构建某种状态对象并解释了内存问题。

这是一个用 cython/pyrex 编写的AIO 绑定(github) 的例子,一个相当花哨的 I/O 的例子。根据我的经验,我们已经使用诸如此类的 I/O 例程将自定义对象以压缩格式编组到磁盘 - 在内存中,cython 代码负责处理 python 可见的内容(例如自定义滚动套接字对象

我最好的建议是让您四处搜索.pyx示例,您会发现一些可以为您提供解决方案的东西。

我也同意其他发帖者的观点:问问自己是否需要迁移到 C,因为扩展类型会增加整个系统的复杂性。

于 2012-11-07T23:06:20.097 回答