我需要用 C 编写一个 Python 扩展,我将用于:
- 对文件执行 CPU 密集型初始化;
- 进行依赖于初始化数据的多个函数调用将结果返回给我;和
- 完成后释放内存
一种解决方案是在 Python 中实现“状态持有者”类。当我在 C 中调用初始化时,它返回我将存储在我的 Python 状态对象中的初始化数据。然后每次我需要执行步骤 (2) 时,我都会将它传递给 C 函数。但是,对于在 Python 端和 C 端之间发生的所有数据传输/接口(interface),这似乎非常低效。
如果可能的话,我想在C端使用状态对象来维护状态。来自 Python 端的初始化调用不会返回所有初始化数据,而只会返回一个 ID,因此它可以在后续调用期间需要时引用 C 状态对象。
我将如何在 C 端维护状态?
最佳答案
首先,我会回答你实际提出的问题。
在 C 中创建一个 struct State
,就像不涉及 Python 时一样。
如果你不打算复制这些(你只通过 struct State *
传递它们),那么你可以做 (intptr_t)theStatePtr
来获得Python 的 id。当然,您确实需要注意 Python 对象的生命周期永远不会超过 C 对象的生命周期,但这是可行的。
如果出于某种原因确实需要复制/移动结构,或者需要更多帮助来管理状态(例如,将 Python id 视为弱引用),请选择适当的集合(哈希表、树、数组等) .) 对于您的用例,然后将 key 作为 id 传递给 Python。
但是,我认为您可能在这里优化了错误的部分。来回传递对象没什么——它只是一个指针副本。重新计数可能是一个问题,但很少会是,而且您从生命周期管理中获得的好处通常是值得的。可能会降低性能的部分是您的 C 代码不断地将一堆 Python 整数转换为 C int
等。如果这是您的问题,只需创建一个具有 C 状态的 C 结构,并将其包装起来在不向 Python 公开任何内部结构的 Python 对象中。
最后,您真的需要在这里进行任何优化吗?如果您正在进行 CPU 密集型工作,我敢打赌实际工作完全掩盖了 Python 对象访问的成本,后者甚至不会出现在分析中。如果您还没有进行概要分析,那绝对是您应该做的第一件事,因为这里的正确答案很可能是“什么都不用做”。
更进一步:如果您只是为了优化而用 C 语言编写 C 代码,您确定您甚至需要那个吗?在 C 中处理内存管理很烦人且容易出错,在 Python 的 C 扩展模块中处理它更是如此,在您还不知道它是如何工作的情况下第一次做它几乎是一个保证花费的秘诀你所有的时间都在追查段错误和泄漏,而不是编写你的实际代码。因此,我会按顺序尝试以下操作,对每个进行分析,只有在速度太慢时才在列表中向下移动:
- 只需用 Python 编写算法,并使用您现有的 CPython 解释器。
- 确保您拥有最佳算法。
- 尝试使用 PyPy 而不是 CPython。
- 获取Cython并尝试以尽可能少的更改编译您的 Python 代码。
- 适本地修改您的代码以利用静态类型、直接调用 C 函数等 Cython 功能。
- 用 C 编写低级代码,用 Cython 或用
ctypes
在 Python 中编写中级代码(跟踪状态对象并向 Python 提供包装器的东西)。 - 使用您最喜欢的界面机制,用 C 语言编写整个中低层。这仍然可能不是 native C API,除非您有丰富的经验并且正在做一些非常简单的事情。
关于Python C 扩展——维护状态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13279329/