0 资料
1 正文
1.1 python执行原理
python执行过程:python脚本语言(.py)或者cython语言(.pyx)–解释器解释–>字节码(.pyc)–虚拟机执行–>机器码(看不到,可以在cpu上跑起来)。
由于没有原生的编译时类型检查,所有的类型的检查都被移交给了运行时,执行一行Python代码很可能需要做不只一行的类型检查、边界检查,因此python比起C++等会慢很多。
python的解释器可以多种,常见的是cpython(最常用)、Ipython(基于cpython的交互式解释器)、pypy(动图编译python代码,运行速度快,与cpython有少数不同)、Jython、IronPython。
1.2 Cypthon(推荐,速度与numba接近)
Cython是一门语言,文件名以.pyx结尾。其是python的超集,即兼容python,Cython与python类似于C++与C的关系。同时Cython也是一个编译器的名称,其可将Cython语言写的pyx文件(包含.py文件)直接编译成动态库,从而获得近乎于写CXX语言的性能。
官网推荐使用setuptools (setup.py)的方法来编译.pyx/.py代码。如以下文件树,
├── os
│ └── ros_os.py
│ └── setup.py
目标是将os文件夹下的ros_os.py编译成.so动态库,因此在ros_os.py同级目录下新建一个setup.py文件。setup.py的内容如下:
from setuptools import setup
from Cython.Build import cythonize
setup(
name='ros_os',
ext_modules=cythonize("ros_os.py"),
zip_safe=False,
)
然后运行指令python setup.py build_ext --inplace,则会在同级目录下生成ros_os.so动态库文件。在其他python文件中,就可以通过import导入该.so文件,实现加速。
注意,若os文件夹下有__init__.py文件,则会出错。解决方法是需要将setup.py移动到与ros_os.py最近的无__init__.py文件的文件夹下,如以下文件树所示:
├── pkg
│ ├── init.py
│ ├── os
│ │ ├── init.py
│ │ └── ros_os.py
└── setup.py
os文件夹下有__init__.py文件,显式地表示os是一个python的包,同样地,pkg下也存在__init__.py文件,因此需要将setup.py放在与pkg同级的目录下,该目录是最靠近ros_os.py的最近的且无__init__.py文件的目录。同时,修改setup.py中对ros_os.py的路径,如下:
from setuptools import setup
from Cython.Build import cythonize
setup(
name='ros_os',
ext_modules=cythonize("pkg/os/ros_os.py"),
zip_safe=False,
)
在setup.py的同级目录下运行编译指令:python setup.py build_ext --inplace,则可以正确编译获得ros_os.so动态文件。
优点:加速python,并达到python加密的效果(推荐的加密手段)
缺点:需要手动编译;少数python内置属性不支持,例如__file__;
1.3 numba(传言可加速40倍左右)
numba是一个可以加速python大部分模块的库,其原理是将其修饰的函数在第一次运行时先优化并翻译成机器码,而在重复运行时,则直接调用该机器码,因此达到可以媲美C和C++的速度。使用方法如下:
from numba import jit # 从numba中导入函数jit
import random
@jit(nopython=True) # jit,numba装饰器中的一种
def monte_carlo_pi(nsamples):
acc = 0
for i in range(nsamples):
x = random.random()
y = random.random()
if (x ** 2 + y ** 2) < 1.0:
acc += 1
return 4.0 * acc / nsamples
在原始代码中加入第1行和第4行,则可以加速monte_carlo_pi模块(自动将其优化并编译成机器码)。即,要加速哪个函数,就在函数定义前面加上装饰器@jit(nopython=True)。
优点:对numpy和循环语法的加速明显;使用方便;
缺点:少量库无法加速,如pandas库;仅能安装到无法用于python2及以下版本;安装比较困难,需要装llvm编译器;
1.4 其他加速方法
- 使用整型代替浮点型
1.5 各自加速方法的对比
代码片段对大小为128x128的二维数组求和,运行1000次时间如下:
Total cost time for func: py_func, call 1000 times: 3.803216s.
Total cost time for func: np_func, call 1000 times: 0.343562s.
Total cost time for func: nb_func, call 1000 times: 0.017122s.
Total cost time for func: cy_func, call 1000 times: 0.018159s.
它们分别代表了原始Python、Numpy、Numba、Cython对应的性能。可以看出,cython与numba可有效加速python代码。其中,numba以稍微快于cython,但是numba不兼容python2,且调试困难,因此,推荐使用cython。