我想问一些关于python解释器的底层原理的问题,因为我自己搜索的过程中并没有得到太多有用的信息。
我最近一直在使用 rust 编写 python 插件,这为 python 的 cpu 密集型任务提供了显着的加速,并且与 c 相比,编写速度也更快。不过它有一个缺点就是,相对于旧的使用cython加速的方案,rust(我使用的是pyo3)的调用开销似乎比c(我使用的是cython)的调用开销更大,
例如,我们在这里得到一个空的 python 函数:
def empty_function():
return 0
在Python中通过for循环调用它一百万次并计算时间,这样我们就可以发现每次调用大约需要70纳秒(在我的电脑中)。
如果我们将其编译为 cython 插件,具有相同的源代码:
# test.pyx
cpdef unsigned int empty_function():
return 0
执行时间将减少至 40 纳秒。这意味着我们可以使用 cython 进行一些细粒度的嵌入,并且我们可以期望它总是比原生 python 执行得更快。
然而,当谈到 Rust 时,(老实说,我现在更喜欢使用 rust 进行插件开发而不是 cython,因为不需要在语法上做一些奇怪的黑客攻击),调用时间将增加到 140 纳秒,几乎是原生蟒蛇。源码如下:
use pyo3::prelude::*;
use pyo3::wrap_pyfunction;
#[pyfunction]
fn empty_function() -> usize {
0
}
#[pymodule]
fn testlib(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(empty_function, m)?)?;
Ok(())
}
这意味着rust不适合对python进行细粒度的嵌入式替换。如果有一个任务的调用时间很少,并且每次调用的时间都比较长,那么使用 Rust 就完美了。然而,如果有一个任务会在代码中被大量调用,那么它似乎不适合 rust ,因为类型转换的开销将占用大部分加速时间。
我想知道这个问题是否可以解决,更重要的是,我想知道造成这种差异的根本原因。在它们之间调用时,cpython解释器是否有某种区别,就像调用c插件时cpython和pypy之间的区别一样?我在哪里可以获得更多信息?谢谢。
===
Update:
抱歉大家,我没想到我的问题会含糊不清,毕竟这三个问题的源代码都已经给出了,并且使用 timeit 来测试函数运行时几乎是 python 开发中的惯例。
我的测试代码与注释中 @Jmb 的代码几乎完全相同,但我使用的有一些细微的差别python setup.py build_ext --inplace
构建方式而不是裸 gcc,但这应该没有任何区别。不管怎样,谢谢补充。