(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 更新了他们的分支,但现在就是这样。
我将 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 更新了他们的分支,但现在就是这样。