迷途小书童的 Note
读完需要
6
分钟速读仅需 2 分钟
1
简介
Python 是一门应用非常广泛的高级语言,但是,长久以来,Python的运行速度一直被人诟病,相比 c/c++、java、c#、javascript 等一众高级编程语言,完全没有优势。
那么真的没有办法提升 Python 程序的运行速度吗?相信看完本文,你应该会有答案。
2
示例
这里以找出 1000000 以内的质数为例,分别计算下需要花费多长的时间?
首先来回顾下什么是质数?
质数(Prime number),又称素数,指在大于 1 的自然数中,除了 1 和该数本身外,无法被其他自然数整除(也可定义为只有 1 与该数本身两个因数)。举个例子,比如说数字 7,从 2 开始一直到 6,都不能被它整除,只有 1 和它本身 7 才能被 7 整除,所以 7 就是一个质数。
下面来看看 python 的代码实现
import math
import time
def is_prime(num):
if num == 2:
return True
if num <= 1 or not num % 2:
return False
# 从3开始,到int(根号num)+1,步长是2,如3,5,7 ...
for i in range(3, int(math.sqrt(num)) + 1, 2):
if not num % i:
return False
return True
def run_program(N):
for i in range(N):
is_prime(i)
if __name__ == '__main__':
N = 1000000
start = time.time()
run_program(N)
end = time.time()
print(end - start)
执行代码,可以看到在我的老旧 i5 机器上总共花费了 5 秒多
3
改进
大家都知道解释型语言,解释器不产生目标机器代码,而是产生中间代码,解释器通常会导致执行效率较低。
因此,问题就变成了,能不能将 python 代码翻译成机器码?那执行效率肯定就会大大提升了
numba 就是这么一款工具,它是 python 的即时编译器(just-in-time compiler),它使用 LLVM 将 python 代码翻译成机器码,特别是在使用 numpy 数组以及循环操作上,效果最佳。
numba 的使用比较简单,我们不需要更换 python 的解释器,只需要将 numba 的装饰器写在 python 方法上,当这个带有 numba 装饰器的方法被调用时,就会被 just-in-time 即时编译为机器代码,然后执行。目前 numba 支持在 X86、ARM 等多种架构上进行动态编译。
使用 numba 之前,我们需要安装这个库
pip install numba
或者
conda install numba
下面来看看 numba 版本的质数问题
import math
import time
from numba import njit
@njit(fastmath=True, cache=True)
def is_prime(num):
if num == 2:
return True
if num <= 1 or not num % 2:
return False
for i in range(3, int(math.sqrt(num)) + 1, 2):
if not num % i:
return False
return True
@njit(fastmath=True, cache=True)
def run_program(N):
for i in range(N):
is_prime(i)
if __name__ == '__main__':
N = 10000000
start = time.time()
run_program(N)
end = time.time()
print(end - start)
执行上述代码,可以看到,速度提升了 4 倍左右,不到 1 秒,效果还是非常明显
最后,作为横向比较,我们使用 c++ 语言,也写一个类似的程序
#include <iostream>
#include <cmath>
#include <time.h>
using namespace std;
bool isPrime(int num)
{
if (num == 2)
return true;
if (num <= 1 || num % 2 == 0)
return false;
double sqrt_num = sqrt(double(num));
for (int i = 3; i <= sqrt_num; i += 2) {
if (num % i == 0)
return false;
}
return true;
}
int main()
{
int N = 1000000;
clock_t start, end;
start = clock();
for (int i=0; i < N; i++)
isPrime(i);
end = clock();
cout << (end - start) / ((double)CLOCKS_PER_SEC);
return 0;
}
编译后执行,可以看到,只花了 0.4 秒
4
小结
从上面的对比示例中可以看到,使用了 just-in-time compiler 后(numba、pypy 都是类似的实现),代码的执行效率已经直逼 C++ 等编译型语言了。
5
参考资料
https://numba.readthedocs.io/en/stable/index.html ( https://numba.readthedocs.io/en/stable/index.html )
6
免费社群