Python可以比C++更快,你不信?

 Python 是一个用途非常广泛的编程语言,拥有成千上万的第三方库,在人工智能、机器学习、自动化等方面有着广泛的应用,众所周知,Python 是动态语言,有全局解释器锁,比其他静态语言要慢,也正是这个原因,你也许会转向其他语言如 Java、C++,不过先等等,今天分享一个可以让 Python 比 C++ 还要快的技术,看完再决定要不要转吧。

创新互联专注于企业全网整合营销推广、网站重做改版、绥德网站定制设计、自适应品牌网站建设、html5购物商城网站建设、集团公司官网建设、外贸网站建设、高端网站制作、响应式网页设计等建站业务,价格优惠性价比高,为绥德等各大城市提供网站开发制作服务。

今天的主角就是 Numba,Numba 是一个开源的即时编译器(JIT compiler),可将 Python 和 NumPy 的代码的转换为快速的机器码,从而提升运行速度。可以达到 C 或 FORTRAN 的速度。

这么牛逼是不是很难用呢?No,No,No,So easy,你不需要替换 Python 解释器,不需要单独编译,甚至不需要安装 C / C ++ 编译器。只需将 Numba 提供的装饰器放在 Python 函数上面就行,剩下的就交给 Numba 完成。举个简单的例子:

 
 
 
 
  1. from numba import jit 
  2. import random 
  3. @jit(nopython=True) 
  4. def monte_carlo_pi(nsamples): 
  5.     acc = 0 
  6.     for i in range(nsamples): 
  7.         x = random.random() 
  8.         y = random.random() 
  9.         if (x ** 2 + y ** 2) < 1.0: 
  10.             acc += 1 
  11.     return 4.0 * acc / nsamples

Numba 是专为科学计算而设计的,在与 NumPy 一起使用时,Numba 会为不同的数组数据类型生成专门的代码,以优化性能:

 
 
 
 
  1. @numba.jit(nopython=True, parallel=True) 
  2. def logistic_regression(Y, X, w, iterations): 
  3.     for i in range(iterations): 
  4.         w -= np.dot(((1.0 / 
  5.               (1.0 + np.exp(-Y * np.dot(X, w))) 
  6.               - 1.0) * Y), X) 
  7.     return w

现在我们来看看,同样的代码,使用 Numba 前后与 C++ 的性能对比。比如说我们要找出 1000 万以内所有的素数,代码的算法逻辑是相同的:

 
 
 
 
  1. Python 代码: 
  2. import math 
  3. import time 
  4. def is_prime(num): 
  5.     if num == 2: 
  6.         return True 
  7.     if num <= 1 or not num % 2: 
  8.         return False 
  9.     for div in range(3, int(math.sqrt(num) + 1), 2): 
  10.         if not num % div: 
  11.             return False 
  12.     return True 
  13. def run_program(N): 
  14.     total = 0 
  15.     for i in range(N): 
  16.         if is_prime(i): 
  17.             total += 1 
  18.     return total 
  19. if __name__ == "__main__":
  20.      N = 10000000 
  21.     start = time.time() 
  22.     total = run_program(N) 
  23.     end = time.time() 
  24.     print(f"total prime num is {total}") 
  25.     print(f"cost {end - start}s")

执行耗时:

 
 
 
 
  1. total prime num is 664579 
  2. cost 47.386465072631836s

C++ 代码如下:

 
 
 
 
  1. #include  
  2. #include  
  3. #include  
  4. using namespace std; 
  5. bool isPrime(int num) { 
  6.     if (num == 2) return true; 
  7.     if (num <= 1 || num % 2 == 0) return false;
  8.     double sqrtsqrt_num = sqrt(double(num)); 
  9.     for (int div = 3; div <= sqrt_num; div +=2){ 
  10.        if (num % div == 0) return false; 
  11.     } 
  12.      return true; 
  13. int run_program(int N){ 
  14.     int total = 0; 
  15.     for (int i; i < N; i++) { 
  16.         if(isPrime(i)) total ++; 
  17.     } 
  18.     return total; 
  19. int main() 
  20.     int N = 10000000; 
  21.     clock_t start,end; 
  22.     start = clock(); 
  23.     int total = run_program(N); 
  24.     end = clock(); 
  25.     cout << "total prime num is " << total; 
  26.     cout << "\ncost " << (end - start) / ((double) CLOCKS_PER_SEC) << "s\n"; 
  27.     return 0; 
 
 
 
 
  1. $ g++ isPrime.cpp -o isPrime 
  2. $ ./isPrime 
  3. total prime num is 664579 
  4. cost 2.36221s

c++

C++ 确实牛逼,才 2.3 秒,不过好戏还在后头,现在我们使用 Numba 来加速一下,操作很简单,不需要改动原有的代码,先导入 Numba 的 njit,再在函数上方放个装饰器 @njit 即可,其他保持不变,代码如下:

 
 
 
 
  1. import math 
  2. import time 
  3. from numba import njit 
  4. # @njit 相当于 @jit(nopython=True)  
  5. @njit 
  6. def is_prime(num): 
  7.     if num == 2: 
  8.         return True 
  9.     if num <= 1 or not num % 2: 
  10.         return False 
  11.     for div in range(3, int(math.sqrt(num) + 1), 2): 
  12.         if not num % div: 
  13.             return False 
  14.     return True 
  15. @njit 
  16. def run_program(N): 
  17.     total = 0 
  18.     for i in range(N): 
  19.         if is_prime(i): 
  20.             total += 1 
  21.     return total
  22. if __name__ == "__main__": 
  23.     N = 10000000 
  24.     start = time.time() 
  25.     total = run_program(N) 
  26.     end = time.time() 
  27.     print(f"total prime num is {total}") 
  28.     print(f"cost {end - start}s")

运行一下,可以看出时间已经从 47.39 秒降低到 3 秒。

 
 
 
 
  1. total prime num is 664579 
  2. cost 3.0948808193206787s

相比 C++ 的 2.3 秒还是有一点慢,你可能会说 Python 还是不行啊。等一等,我们还有优化的空间,就是 Python 的 for 循环,那可是 1000 万的循环,对此,Numba 提供了 prange 参数来并行计算,从而并发处理循环语句,只需要将 range 修改为 prange,装饰器传个参数:parallel = True,其他不变,代码改动如下:

 
 
 
 
  1. import math 
  2. import time 
  3. from numba import njit, prange 
  4. @njit 
  5. def is_prime(num): 
  6.     if num == 2: 
  7.         return True 
  8.     if num <= 1 or not num % 2:
  9.         return False 
  10.     for div in range(3, int(math.sqrt(num) + 1), 2): 
  11.         if not num % div: 
  12.             return False 
  13.     return True 
  14. @njit(parallel = True) 
  15. def run_program(N): 
  16.     total = 0 
  17.     for i in prange(N): 
  18.         if is_prime(i): 
  19.             total += 1 
  20.     return total 
  21. if __name__ == "__main__": 
  22.     N = 10000000 
  23.     start = time.time() 
  24.     total = run_program(N) 
  25.     end = time.time() 
  26.     print(f"total prime num is {total}") 
  27.     print(f"cost {end - start}s")

现在运行一下:

 
 
 
 
  1. python isPrime.py 
  2. total prime num is 664579 
  3. cost 1.4398791790008545s

才 1.43 秒,比 C++ 还快,Numba 真的牛逼!我又运行了两次,确认自己没看错,平均就是 1.4 秒:

Python

看到这里,Numba 又让我燃起了对 Python 的激情,我不转 C++ 了,Python 够用了。

Numba 如何做到的呢?官方文档这样介绍:它读取装饰函数的 Python 字节码,并将其与有关函数输入参数类型的信息结合起来,分析和优化代码,最后使用编译器库(LLVM)针对你的 CPU 生成量身定制的机器代码。每次调用函数时,都会使用此编译版本,你说牛逼不?

Numba 还有更多详细的用法,这里不多说,想了解的请移步官方文档[1]。

最后的话

Python 几乎在每一个领域都有对应的解决方案,本文提到的 Numba 库就是专门解决 Python 在计算密集型任务方面性能不足的问题,如果你从事机器学习、数据挖掘等领域,这个会非常有帮助,如果本文对你有用,请点赞、在看、关注支持。

网站栏目:Python可以比C++更快,你不信?
分享链接:http://www.shufengxianlan.com/qtweb/news22/80122.html

网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联