想问一些关于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之间的区别?我在哪里可以获得更多信息?谢谢。
===
更新:
抱歉各位,没想到我的问题会含糊不清,毕竟这三个的源代码都已经给出了,使用timeit来测试函数运行时几乎是python开发中的惯例。
我的测试代码与注释中的@Jmb 代码几乎完全相同,但我使用python setup.py build_ext --inplace
构建方式而不是裸 gcc 的方式存在一些细微差别,但这不应该有任何区别。无论如何,谢谢你的补充。