真是哭了。。。为什么我之前写的numba教程全没了啊啊啊啊啊啊!!!!?????
只能从头整理了无语。
How Numba and Cython speed up Python coderushter.com https://blog.csdn.net/helloxiaozhe/article/details/78104975blog.csdn.net参考链接见上。
首先回顾一下编译型语言和解释型语言(也经常叫脚本语言):
1、编译型语言,C、C++、Fortran、Pascal、Ada,由编译型语言编写的源程序需要经过编译,汇编和链接才能输出目标代码,然后由机器执行目标代码。目标代码是有机器指令组成,不能独立运行,因为源程序中可能使用了一些汇编程序不能解释引用的库函数,而库函数又不在源程序中,此时还需要链接程序完成外部引用和目标模板调用的链接任务,最后才能输出可执行代码。(例如我们编写c的时候引用了一个自定义的c库函数,则需要进行链接操作)
2、解释型语言,解释器不产生目标机器代码,而是产生中间代码,这种中间代码与机器代码不同,中间代码的解释是由软件支持的,不能直接使用在硬件上。该软件解释器通常会导致执行效率较低,用解释型语言编写的程序是由另一个可以理解中间代码的解释程序执行的。和编译的程序不同的是, 解释程序的任务是逐一将源代码的语句解释成可执行的机器指令,不需要将源程序翻译成目标代码再执行。对于解释型语言,需要一个专门的解释器来执行该程序,每条语句只有在执行是才能被翻译,这种解释型语言每执行一次就翻译一次,因而效率低下
一、python编译过程概述
当我们执行Python代码的时候,在Python解释器用四个过程“拆解”我们的代码,最终被CPU执行返回给用户。
首先当用户键入代码交给Python处理的时候会先进行词法分析,例如用户键入关键字或者当输入关键字有误时,都会被词法分析所触发,不正确的代码将不会被执行。
下一步Python会进行语法分析,例如当"for i in test:"中,test后面的冒号如果被写为其他符号,代码依旧不会被执行。
下面进入最关键的过程,生成字节码,在执行Python前,Python会生成.pyc文件,这个文件就是字节码,如果我们不小心修改了字节码,Python下次重新编译该程序时会和其上次生成的字节码文件进行比较,如果不匹配则会将被修改过的字节码文件进行覆盖,以确保每次编译后字节码的准确性。
那么什么是字节码?字节码在Python虚拟机程序里对应的是PyCodeObject对象。.pyc文件是字节码在磁盘上的表现形式。简单来说就是在编译代码的过程中,首先会将代码中的函数、类等对象分类处理,然后生成字节码文件。有了字节码文件,通过python的解释器对字节码进行解释,将每一行的字节码解释成CPU可以直接识别的机器码,执行。
(补充:python解释器的类型有cpython、IPython、PyPy、Jython、IronPython:
Python解释器www.liaoxuefeng.com)
所以,综上所属,python的整个从输入到输出的过程如下图:
我们常见的cpython解释器是用c语言的方式来解释字节码的,而numba则是使用LLVM编译技术来解释字节码的。
首先我们来看看numba的简单使用案例与加速情况:
import numpy as np
from numba import jit
a = np.arange(1, 10 ** 7)
b = np.arange(-10 ** 7, -1)
@jit(nopython=True)
def sum_sequence(a, b):
result = np.zeros_like(a)
for i in range(len(a)):
result[i] = a[i] - b[i]
return result
然后我们通过dis进行反编译得到python的字节码:
>>> import dis
>>> dis.dis(sum_sequence)
2 0 LOAD_GLOBAL 0 (np)
2 LOAD_ATTR 1 (zeros_like)
4 LOAD_FAST 0 (a)
6 CALL_FUNCTION 1
8 STORE_FAST 2 (result)
3 10 SETUP_LOOP 40 (to 52)
12 LOAD_GLOBAL 2 (range)
14 LOAD_GLOBAL 3 (len)
16 LOAD_FAST 0 (a)
18 CALL_FUNCTION 1
20 CALL_FUNCTION 1
22 GET_ITER
>> 24 FOR_ITER 24 (to 50)
26 STORE_FAST 3 (i)
4 28 LOAD_FAST 0 (a)
30 LOAD_FAST 3 (i)
32 BINARY_SUBSCR
34 LOAD_FAST 1 (b)
36 LOAD_FAST 3 (i)
38 BINARY_SUBSCR
40 BINARY_SUBTRACT
42 LOAD_FAST 2 (result)
44 LOAD_FAST 3 (i)
46 STORE_SUBSCR
48 JUMP_ABSOLUTE 24
>> 50 POP_BLOCK
5 >> 52 LOAD_FAST 2 (result)
54 RETURN_VALUE
我们之前写的cython本质上还是通过c的编译器来替换掉cpython底层的复杂代码从而实现了加速(比如python的动态类型,涉及到一大堆的类型检查、多态、溢出检查等耗时非常多,但是如果使用cython的静态类型就没有这么多麻烦的问题),
而numba的思路则不太一样,numba是在一个叫做LLVM的编译器上进行编译,Numba将Python字节码转换为LLVM中间表示(IR),请注意,LLVM IR是一种低级编程语言,与汇编语法类似,与Python无关。
Numba模式
Numba有两种模式:nopython
和object
。前者不使用Python运行时并生成没有Python依赖的本机代码。本机代码是静态类型的,运行速度非常快。而对象模式使用Python对象和Python C API,而这通常不会显着提高速度。在这两种情况下,Python代码都是使用LLVM编译的。
什么是LLVM?
LLVM是一个编译器,它采用代码的特殊中间表示(IR)并将其编译为本机(机器)代码。编译过程涉及许多额外的传递,其中LLVM编译器可以优化IR。LLVM工具链非常擅长优化IR,因此它不仅可以编译Numba的代码,还可以优化它。
整个系统大致如下:
Numba的优点:
- 便于使用
- 自动并行化
- 支持numpy操作和对象
- 支持调用GPU
Numba的缺点:
- debug非常麻烦
- 无法在
nopython
模式下与Python及其模块进行交互,numba目前在nopython模式下支持python模块有限,比如pandas是不支持的,但是不支持意味着无法加速并不意味着不能运行。 - 对python中的类class支持有限