def add(a,b):
return a +b
对于这个python代码 两种编译器分别做了两种处理
CPython 执行起来大概是这样(伪代码):
if instance_has_method(x, '__add__') {
return call(x, '__add__', y) // x.__add__ 里面又有一大堆针对不同类型的 y 的判断
} else if isinstance_has_method(super_class(x), '__add__' {
return call(super_class, '__add__', y)
} else if isinstance(x, str) and isinstance(y, str) {
return concat_str(x, y)
} else if isinstance(x, float) and isinstance(y, float) {
return add_float(x, y)
} else if isinstance(x, int) and isinstance(y, int) {
return add_int(x, y)
} else ...
以此来完成Python 的动态类型 执行到这里还没完 还得实现C语言的部分
实际上Python 里面的一个int 大概是个这样的结构体
struct {
prev_gc_obj *obj
next_gc_obj *obj
type int
value int
... other fields
}
然后每个int都是这样的结构体 还是动态分配出来放在heap上的 里面的value还不能变 每次计算出来的结构体 还要去heap上malloc 一个结构体 CPython每次重复这个过程
而PyPy执行的时候 得益于JIT 第一次 传入两个整数 就生成两个整数相加的函数的C代码 下次再传入两个整数时 直接 加载以及生成的方法 当然比CPython快
但是JIt 本身也是有问题的 编译本身需要花很多时间 如果这个代码本来只执行一次 需要1s 但是编译需要10s
PyPy 源代码文件分为两部分 一部分完全是为了生成 编译工具链 另一部分是关键的解释器部分 他是混有RPython的 以及给工具链提供编译的普通Python的部分
实际编译步骤是 先用CPyton 或别的Python解释器 import RPython的编译工具链
工具链是普通的Python 这个Python import 了RPython代码到内存里 然后对RPython代码做静态分析和类型推导 推导完的结果 生成等价的C代码 然后调用gcc编译生成本地diamagnetic 然后我们就得到一个可执行的pypy-c文件 然后用pypy-c文件来执行开发者的python代码
而CPython 是两层Python
它直接用CPython调用PyPy.py 做自解释 CPython会直接把PyPy的RPython的代码当作普通Python代码执行