Python运行时:重新编译和重用C库

4 投票
3 回答
561 浏览
提问于 2025-04-16 21:40

我正在开发一个工具,用于对用户自定义的函数进行一些数值分析。这个工具的想法是用Python做一个方便的界面,用户可以在里面输入C语言的函数,然后按一个按钮,就能得到一些输出数据。因为计算可能需要几分钟甚至几个小时,所以仅仅使用Numpy的性能是无法接受的。

我尝试了以下方法:这个基于Python的界面调用gcc,编译用户的函数生成一个dll文件,然后我的核心算法用Cython封装后调用这个dll。这个方法是可行的,但由于无法完全卸载Python模块,我不能在整个界面程序关闭并重新启动之前重新编译用户定义的函数。

我现在看到的唯一解决办法是将计算核心和界面进程分开,然后通过共享内存或消息传递让它们进行交互。当用户想要更新他的函数时,程序就终止计算核心,重新编译dll,然后再启动计算核心。

你能建议一下在这种情况下的常见做法吗?

谢谢!

3 个回答

1

你有没有考虑过 Weave 或者 Instant更新不太及时)?我自己只用过 Instant,但这两个工具似乎都非常适合你所描述的需求。

这两个工具都可以在运行时自动编译和缓存 C 语言代码,直接从字符串中获取。我自己只用过 Instant 来嵌入 C 代码,但我相信 Weave 或者 Instant 也能很好地处理用户输入的编译。

下面是一个使用 Weave 的例子,来自于 教程

>>> a = 'string'
>>> def protected_printf(a):
...     assert(type(a) == type(1))
...     weave.inline(r'printf("%d\n",a);',['a'])
>>> protected_printf(1)
 1
>>> protected_printf('string')
AssertError...
2

每次用户输入一个函数时,你可以生成一个新的模块,给它起个随机且独特的名字(就像你为临时文件做的那样)。然后编译这个模块,加载它,并调用这个函数。

随着时间的推移,这样做会导致加载很多模块。如果用户在一次会话中调用的函数不太多,你可能还可以应付得了。另一种方法是把函数编译成一个普通的DLL,而不是Python模块,然后用ctypes来加载它。当你用完这个DLL后,只需丢掉对它的任何引用——当ctypes的句柄最终被垃圾回收时,它应该会卸载这个DLL(具体可以参考 如何使用ctypes在Python中卸载DLL?)。

你可能还想从磁盘上删除你的DLL。如果你使用的是Windows,这可能会有点麻烦,因为很难预测ctypes何时会卸载它,而Windows在DLL被使用时是无法删除它的。

4

Python对多进程的支持非常好(而对多线程的支持就不太行),所以你可以为每个需要计算的表达式启动一个新的Python进程。在这个新进程中,你可以编译和加载dll文件,然后让它把结果传回给主进程。当这个新进程结束时,所有的东西都应该被卸载掉。

撰写回答