我想问一些关于python解释器的基本原理的问题,因为我在自己的搜索中没有得到太多有用的信息。
我最近一直在使用rust编写python插件,这给python的cpu密集型任务提供了显著的加速,与c相比,它也更快。然而它有一个缺点是,与使用cython加速的旧方案相比,rust(我使用pyo3)的调用开销似乎大于c(我使用cython),
例如,我们在这里得到一个空的python函数:def empty_function():
return 0
在Python中通过for循环调用它一百万次并计算时间,这样我们就可以发现每次调用大约需要70纳秒(在我的pc中)。
如果我们把它编译成一个cython插件,使用相同的源代码:
# test.pyx
cpdef unsigned int empty_function():
return 0
执行时间将减少到40纳秒。这意味着我们可以使用python进行一些细粒度的嵌入,并且我们可以期望它总是比本地python执行得快。
然而,当涉及到Rust时,(老实说,我现在更喜欢使用Rust而不是cython进行插件开发,因为不需要在语法上做一些奇怪的修改),调用时间将增加到140纳秒,几乎是原生python的两倍。源代码如下:
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,但这应该没有任何区别。不管怎样,谢谢你的补充。
正如评论中建议的那样,这是一个自我回答。
由于评论部分的讨论没有得出明确的结论,所以我在pyo3的repo中提出了一个问题,并从谁的主要维护者那里得到了回应。
简而言之,结论是pyo3和cython编译的插件在cpython调用它们时没有根本的区别。当前的速度差异来自于优化深度的不同。
这是问题的链接:https://github.com/PyO3/pyo3/issues/1470
这里还值得注意的是,用python setup.py build_ext --inplace
编译rust扩展将它们构建为非优化模式(python setup.py develop
或pip install -e .
也是如此)。
以下是命令
的输出Finished dev [unoptimized + debuginfo] target(s) in 0.02s
内置"release"模式,使用:
pip install .
使用pip install . --verbose
,您可以看到差异:
Finished release [optimized] target(s) in 1.02s
这可以使大量差异,在我的情况下,未优化的构建比优化的构建慢9倍。