(JIT) 使用 FFI 库调用编译 Python 代码

(JIT) Compilation of Python code with FFI library calls

我将 Python 与一个库一起使用,该库使用 cppyy 来访问 Python 中的大量 C++ 函数。但是,对 cppyy 方法的调用会花费大量时间,并且使用库调用循环 Python 意味着开销成为一个严重的问题。这是我的意思的一个例子:

import ROOT  # Library based on cppyy

def some_function():
    for i in range(10_000_000):
        ROOT.some_method(i)  # A little bit of overhead * 10_000_000 is a lot of overhead

如果我直接在 C++ 中使用 ROOT 库,这段代码会非常快,但是 Python 真的很慢。

有没有办法快速将其 JIT 编译为 运行,编译后的版本不使用 Python 运行 时间(如 numba nopython=True)?

some_method 中只需要做很少的工作,cppyy 开销就很重要了……请注意,还有方法查找的开销(即 ROOT. 位) Python 中的迭代器协议本身也不可忽略,如果 cppyy 开销不是(计时一个空循环,我发现它占总开销的 20%)。

如果这是一个紧密的内部循环,为避免所有这些情况,只需将其反转并 运行 在 C++ 中。例如。比较这些:

import cppyy
import time

N = 10000000

cppyy.cppdef("""\
void cppmethod(int i) {
   /* empty */
}""")

f = cppyy.gbl.cppmethod

ts = time.perf_counter()
for i in range(N):
    f(i)
te = time.perf_counter()-ts
print("python loop:", te)

cppyy.cppdef("""\
template<typename T>
void call_cppmethod(T f, int N) {
    for (int i = 0; i < N; ++i) f(i);
}""")

ts = time.perf_counter()
cppyy.gbl.call_cppmethod(f, N)
te = time.perf_counter()-ts
print("callback loop:", te)

在我的笔记本电脑上,回调比 Python 循环快 100 倍。但是,如果我在 cppmethod 中做某事,例如。调用三角函数,差异已经下降到 10 倍(同样,一个好的块只是 python 循环)。

可以肯定的是,上面对回调模板的使用在 cppyy 中有效。然而,ROOT 包含一个 fork 的 cppyy,它已经过时了。在那里你不能使用模板并且必须明确函数类型:

cppyy.cppdef("""\
void call_cppmethod(void (*f)(int), int N) {
    for (int i = 0; i < N; ++i) f(i);
}""")

这个问题可能会在未来的某个时候消失if/when ROOT 更新了他们的分支,但现在就是这样。