【问题标题】:(JIT) Compilation of Python code with FFI library calls(JIT) 使用 FFI 库调用编译 Python 代码
【发布时间】:2021-06-28 13:47:02
【问题描述】:

我将 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 运行时(如numbanopython=True)?

【问题讨论】:

  • 你确定这个简单的循环是性能瓶颈吗?无论如何,您是否尝试使用numba?也可以从cppyy 包装器中提取实际函数指针,创建一个ctypes 函数,如果numba 可以自己完成,则该函数由numba 集成。

标签: python performance jit numba


【解决方案1】:

必须在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 包含一个已经过时的 cppyy 的 fork。在那里您不能使用模板,并且必须明确说明函数类型:

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

如果/当 ROOT 更新他们的分叉时,这个问题可能会在未来的某个时候消失,但现在就是这样。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-03-23
    • 2017-04-10
    • 1970-01-01
    • 2021-09-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多