1.python执行原理
python执行过程:python脚本语言(.py)或者cython语言(.pyx)–解释器解释–>字节码(.pyc)–虚拟机执行–>机器码(看不到,可以在cpu上跑起来)。
由于没有原生的编译时类型检查,所有的类型的检查都被移交给了运行时,执行一行python代码很可能需要做不只一行的类型检查、边界检查,因此python比起c++等会慢很多。
python的解释器可以多种,常见的是cpython(最常用)、ipython(基于cpython的交互式解释器)、pypy(动图编译python代码,运行速度快,与cpython有少数不同)、jython、ironpython。
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 cythonizesetup( 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__;
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编译器;
4.其他加速方法
使用整型代替浮点型
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。
6.方法补充
下面小编为大家整理了一些其他python可以加速运行的技巧,希望对大家有所帮助
1.全面加速(pypy)
将python换为pypy,在纯python代码下,pypy的兼容性就不影响使用了,因为一些纯python的代码常常会用pypy进行一下加速
测试代码,for循环10000000次
start = time.time() for i in range(10000000): print(i,end="\r") end = time.time() print(f"耗费时间{end-start}秒>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>")
2.if判断靠前
如:
if tag in ["nts", "nto", "ntc", "ntcb", "ntcf", "ntch", "nth", "ntu", "nt"]: bmes(f_,i2, tag="org") elif tag in ["nb", "nba", "nbc", "nbp", "nf", "nm", "nmc", "nhm", "nh"]: bmes(f_,i2, tag="obj") elif tag in ["nnd", "nnt", "nn"]: bmes(f_,i2, tag="job") elif tag in ["nr", "nrf"]: bmes(f_,i2, tag="per") elif tag in ["t"]: bmes(f_,i2, tag="time") elif tag in ["ns", "nsf"]: bmes(f_,i2, tag="loc") else: for i3 in list(i2): f_.write(i3 + " " + f"o" + "\n")
满足条件的可以先跳出判断
到此这篇关于python代码加速运行的四种方法详解的文章就介绍到这了,更多相关python加速运行内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论