Python C扩展 - 维护状态

时间:2012-11-07 22:26:13

标签: python python-c-extension

我需要在C中编写一个Python扩展,我将用于:

  1. 对文件执行CPU密集型初始化;
  2. 进行多个函数调用,依赖初始化的数据将结果返回给我;和
  3. 我完成后的自由记忆
  4. 一种解决方案是在Python中实现“状态持有者”类。当我在C中调用初始化时,它会返回我将存储在Python状态对象中的初始化数据。然后每当我需要执行步骤(2)时,我会将它传递给C函数。但是,在Python端和C端之间发生所有数据穿梭/接口时,这似乎效率很低。

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

    我如何在C方面维持状态?

2 个答案:

答案 0 :(得分:3)

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

在C中创建struct State,就像没有参与Python一样。

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

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

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

最后,你真的需要在这里进行任何优化吗?如果你正在进行CPU密集型工作,我敢打赌真正的工作会彻底掩盖Python对象访问的成本,而后者甚至不会在分析中显示出来。如果你还没有描述过,那绝对是你应该做的第一件事,因为这里的正确答案可能就是“不要做任何事情”。

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

  • 只需在Python中编写算法,并使用现有的CPython解释器。
  • 确保您拥有最佳算法。
  • 尝试PyPy而不是CPython。
  • 获取Cython并尝试使用尽可能少的更改来编译Python代码。
  • 根据需要修改代码以利用Cython功能,如静态类型,直接调用C函数等。
  • 在C语言中编写低级代码,使用ctypes在Cython或Python中编写跟踪状态对象并向Python提供包装的内容代码。
  • 使用您喜欢的界面机制在C中写下整个中低级别。哪个仍然可能不是本机C API,除非你有很多经验并且做的事情非常简单。

答案 1 :(得分:0)

查看Cython以便轻松进行python-to-C桥接。那里的文档有很多例子 - 我链接到一个页面,它解释了如何构建某种状态对象,并解释了内存问题。

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

我最好的建议是,您可以搜索.pyx示例,并找到一些可以为您提供解决方案的内容。

我也同意其他海报:问问自己,转移到C是否是必需的东西,因为扩展类型会增加整个系统的复杂性。