深入解析PyPy:架构原理、性能优势与C扩展兼容性
本文深入解析PyPy架构,阐述其由RPython实现的解释器与支持多后端的编译器两部分组成。通过对比CPython、Pyston与PyPy的性能测试数据,展示PyPy在纯Python代码中的显著速度优势。同时分析PyPy在C/C++混合编程场景下的局限性,介绍ctypes与cffi两种扩展方式,并指出当前PyPy在C扩展兼容性方面仍存在的挑战。
PyPy是一个虚拟机项目,其核心架构主要包含两个关键部分:一个是基于Python实现的解释器,另一个是用于编译该解释器的编译器。
PyPy的第一部分:基于Python实现的Python解释器
严格来说,这种描述并不完全准确。更精确的说法是,它是使用rPython(Python的一个子集)实现的Python解释器。尽管rPython并非完整的Python语言,但利用rPython编写的这个解释器却能够解释并执行完整的Python代码。
PyPy的第二部分:编译器
这是一个专门用于编译rPython的编译器。该编译器目前仅包含一个rPython前端,但拥有多个后端,这意味着它支持生成多种目标语言的代码。其中较为重要的目标语言包括C语言、CIL(公共语言基础设施)以及JavaScript等。
为了便于理解,我们可以将第一部分称为pypy(1),将第二部分称为pypy(2)。
为什么需要在同一层面同时使用这两者?我们可以这样理解:pypy(1)是一个用RPython编写的解释器,它能够加载用户的Python代码并将其编译为字节码。然而,这个用RPython编写的解释器本身也需要被另一个Python实现来解释执行。虽然我们可以直接使用CPython来运行这个解释器,但这种方式速度不够快。因此,我们采用pypy(2)来编译这个PyPy解释器,生成针对其他平台(如C、JVM或CLI)的代码,并在我们的机器上运行。此外,pypy(2)还引入了JIT(即时编译)特性,能够将字节码转换为机器语言。PyPy之所以运行速度快,正是因为它整合了带有JIT跟踪技术的优化编译器。
PyPy性能测试分析


本次测试对比了CPython 2.7.6、Pyston 0.2以及PyPy 2.2.1的性能。测试使用了Pyston源代码目录下的minibenchmarks和microbenchmarks中的Python代码,具体对比结果如下表所示。
|
|
Cpython2.7.6 |
pyston0.2 |
|
|
microbenchmarks |
|
|
|
|
attribute_lookup.py |
258.544s |
200.387s |
2.667s |
|
attrs.py |
0.622s |
1.658s |
0.086s |
|
closures.py |
0.485s |
6.658s |
0.058s |
|
empty_loop.py |
3.532s |
19.248s |
0.248s |
|
fib2.py |
3.375s |
0.669s |
0.804s |
|
fib.py |
3.696s |
0.636s |
0.864s |
|
function_calls.py |
5.283s |
0.878s |
0.303s |
|
gcj_2014_2_b.py |
1.527s |
45.803s |
0.276s |
|
gcj_2014_3_b.py |
0.022s |
0.174s |
0.069s |
|
iteration.py |
0.185s |
1.242s |
0.062s |
|
lcg.py |
2.910s |
9.097s |
0.235s |
|
listcomp_bench.py |
10.132s |
56.170s |
1.379s |
|
nested.py |
0.368s |
6.828s |
0.057s |
|
polymorphism.py |
4.358s |
4.390s |
14.260s |
|
prime_summing.py |
20.197s |
43.779s |
1.250s |
|
pydigits.py |
0.034s |
Failed |
0.039s |
|
repatching.py |
0.475s |
0.384s |
0.061s |
|
simple_sum.py |
0.075s |
0.578s |
0.040s |
|
sort.py |
2.216s |
4.587s |
0.135s |
|
thread_contention.py |
6.486s |
8.133s |
0.240s |
|
thread_uncontended.py |
1.324s |
5.823s |
0.238s |
|
unwinding.py |
1.082s |
93.180s |
4.481s |
|
vecf_add.py |
9.890s |
Failed |
0.059s |
|
vecf_dot.py |
4.944s |
8.434s |
0.062s |
|
|
|
|
|
|
minibenchmarks |
|
|
|
|
allgroup.py |
0.836s |
Failed |
18.804s |
|
chaos.py |
26.268s |
Failed |
1.392s |
|
fannkuch_med.py |
0.990s |
1.898s |
0.325s |
|
fannkuch.py |
10.952s |
20.834s |
2.057s |
|
Go.py |
53.787s |
Failed |
33.638s |
|
interp2.py |
5.521s |
10.124s |
0.701s |
|
interp.py |
10.863s |
5.035s |
0.563s |
|
nbody_med.py |
3.132s |
6.642s |
0.601s |
|
nbody.py |
12.677s |
25.540s |
1.470s |
|
nq.py |
29.879s |
Failed |
44.418s |
|
raytrace.py |
11.608s |
Failed |
1.228s |
|
spectral_norm.py: |
14.388s |
118.309s |
1.333s |
从PyPy编译的结果来看,除了带有颜色背景的数据项外,其他测试结果基本均为最快。其中,有15个程序的测试耗时不到CPython的十分之一,显示出显著的性能优势。
PyPy的缺陷分析
可以看出,PyPy在实现Python方面具有巨大优势,但目前许多公司的Python项目仍未采用PyPy,主要原因在于其存在的缺陷。
PyPy的一个主要缺陷是C扩展性较弱。简单来说,如果Python程序中混合了C/C++代码,并调用了C/C++库,就会导致PyPy
不支持该扩展或运行速度大幅降低。而当前许多项目都采用C/C++与Python混合编程的模式,这使得该缺陷影响较大。
不过,PyPy也提供了兼容C/C++的方法(尽管并未完全解决扩展性弱的问题)。PyPy提供了ctypes和cffi两种方式进行C扩展,以下是一些简单的程序实验。
进行C扩展的简单程序实验:
使用ctypes方式实现C++与Python混合编程。首先编写一个.cpp文件,然后在Python文件中调用它。最后分别使用CPython和PyPy编译执行,两者均可正常运行,这说明ctypes是支持C++扩展的。



这次使用PyPy运行,速度比CPython要快不少。
总结
以上就是本文关于PyPy的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站其他相关专题,如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













