何为计算密集型任务

下面贴上网上找到的描述计算密集型任务的特点是要进行大量的计算,消耗CPU资源,比如计算圆周率、对视频进行高清解码等等,全靠CPU的运算能力。这种计算密集型任务虽然也可以用多任务完成,但是任务越多,花在任务切换的时间就越多,CPU执行任务的效率就越低,所以,要最高效地利用CPU,计算密集型任务同时进行的数量应当等于CPU的核心数。

计算密集型任务由于主要消耗CPU资源,因此,代码运行效率至关重要。Python这样的脚本语言运行效率很低,完全不适合计算密集型任务。对于计算密集型任务,最好用C语言编写。

简单总结一下就是,计算密集型任务吃CPU。Python是脚本语言效率低,做计算密集型任务很弱。可以用mutliprocess的方式在一定程度内提升运行效率。但还是不如C。

C语言是编译型语言,需通过编译器(compiler)将源代码编译成机器码,之后才能执行的语言。一般需经过编译(compile)、链接(linker)这两个步骤。编译是把源代码编译成机器码,链接是把各个模块的机器码和依赖库串连起来生成可执行文件。因为编译只做一次,运行时不需要编译,所以编译型语言的程序执行效率高。可以脱离语言环境独立运行。而Python是解释型语言,解释性语言的程序不需要编译,相比编译型语言省了道工序,解释性语言在运行程序的时候才逐行翻译。但是每次运行的时候都要解释一遍,性能上不如编译型语言。

所以拿Python来和C比性能可以说是拿鸡蛋碰石头。但是由于Python语言的简洁性、易读性以及可扩展性,越来越多的领域如Web开发、科学计算和统计、人工智能等等等都可以见到Python的身影。那么假如由于某些原因而不得不选择使用Python作为计算密集型任务的开发语言时,该如何提升Python程序的运行效率呢?

应对计算密集型任务比较通用的方法是多进程,这里不做过多讨论,感兴趣的同学可以自行搜索。今天要介绍的主角是Cython。

什么是Cython

Cython是让Python脚本支持C语言扩展的编译器,Cython能够将Python+C混合编码的.pyx脚本转换为C代码,主要用于优化Python脚本性能或Python调用C函数库。由于Python固有的性能差的问题,用C扩展Python成为提高Python性能常用方法,Cython算是较为常见的一种扩展方式。

计算密集型任务例子

计算一定范围内哪些数字是素数是一个典型的计算密集型任务。我们就以计算300万以内的素数列表为例,来看如何用Cython给代码提速。

Python实现

首先来看一下纯Python的实现。

import time

from math import sqrt

def primes(n):

results = [1]

for i in range(2, n):

for j in range(2, int(sqrt(i))):

if i % j == 0:

break

else:

results.append(i)

return results

def main():

delta_time = []

for _ in range(10):

start = time.time()

primes(3000000)

end = time.time()

delta_time.append((end - start))

print('Average Running Time: {} Seconds'.format(sum(delta_time) / 10))

if __name__ == '__main__':

main()

代码挺简单的就不做赘述了。跑了10次primes()最终得到的程序平均运行时间为18.07s。

直接使用Cython

接下来让我们尝试不对源代码做任何修改,直接用Cython将其编译成C是什么效果。

首先直接将python文件后缀改成pyx

cp prime.py prime_c.pyx

相同目录下新建一个setup.py

from distutils.core import setup

from Cython.Build import cythonize

setup(

ext_modules = cythonize('prime_c.pyx')

)

运行命令将Python编译为二进制代码

python setup.py build_ext --inplace

这会在当前目录下生成这2个文件 prime_c.c : 调用Python源码的C代码 prime_c.cpython-39-darwin.so : 把C代码编译成动态链接库

这样我们就可以用python调用刚才生成的动态连接库

import prime_c

prime_c.main()

最终的平均执行时间为12.11s。程序的执行速度有了32.98%的小幅提升,但还不是很快。

为了进一步提升代码效率,我们需要进一步深入代码来找找可以优化的地方。然而刚才生成的C代码长这样,可读性很差。

好在Cython提供了一个分析生成code的方法。通过 cython -a prime_c.pyx可以生成一个prime_c.html。在这个网页里可以直观看到python源码和c代码的对应关系。

越黄的部分意味着越多的调用python虚拟机,越白意味着越接近non-Python的C代码。因此我们的目标是尽可能减少黄色,增加白色。但是这不意味着减少黄色的部分就能极大的提升程序效率,因为每次调用Python虚拟机都会有一定开销,但单次请求的开销其实并不太大,只有在一个大循环里反复调用虚拟机开销才比较明显。所以我们可以忽略循环外的代码,而将注意力集中在循环的部分。

当n等于300万时,程序的主要时间开销就在下面这个循环里。

for i in range(2, n):

for j in range(2, int(sqrt(i))):

if i % j == 0:

break

else:

results.append(i)

进一步优化

Python的运行速度慢除了因为是解释执行以外还有一个最重要的原因是Python是动态类型语言,每个变量在运行前是不知道类型是什么的,所以即便编译为二进制代码同样速度不会太快,这时候我们需要使用Cython来指定Python的数据类型。

修改我们的pyx文件,给参数n加上类型int。同时声明用于循环的i,j也为int类型。

import time

from math import sqrt

def primes(int n):

cdef int i, j

results = [1]

for i in range(2, n):

for j in range(2, int(sqrt(i))):

if i % j == 0:

break

else:

results.append(i)

return results

def main():

delta_time = []

for _ in range(10):

start = time.time()

primes(3000000)

end = time.time()

delta_time.append((end - start))

print('Average Running Time: {} Seconds'.format(sum(delta_time) / 10))

if __name__ == '__main__':

main()

做了这么一点微小的改动后可以看到代码变白了一点。之后按照之前的方法编译运行。

猜猜这么一个微小的改动能让速度提升多少?答案揭晓……最终的平均运行时间为1.22s,比纯python提升了93.25%,速度是原来的15倍左右!

程序还可以进一步优化,比如可以用numpy.array代替原生List,这里就不多说了,感兴趣的同学可以尝试一下。

总结

人生苦短,我用Python。除了Cython还有诸如Shed Skin、Numba、Pythran、PyPy等工具可以从多方面提升Python的运行程序的运行效率。不过语言只是工具,针对具体业务制定高效的方案才是王道。

特修斯之Python——一个可以在电脑里运行的程序,只要一段代码效率低了,那么这段代码就会被替换掉,那么问题来了,如果这个程序全被替换成C了,最终产生的这程序是否还是原来的那个程序,还是一个完全不同的程序?

开个玩笑。对Python性能感兴趣的同学可以看看《Python高性能编程》这本书进行深入了解。

References

python计算密集型提速_利用Cython加速计算密集型python任务相关推荐

  1. python用泰勒级数计算圆周率_利用泰勒级数加速计算

    使用Python+Numpy,它可能在这里和那里进行了优化,因此不可能真正地对log(1+x)与{}进行基准测试. 所以我搬到了C++.在 结果:Time per operation with log ...

  2. python网页版本_利用jupyter网页版本进行python函数查询方式

    我就废话不多说了,还是直接看代码吧! import numpy world_alchol=numpy.genfromtxt("world_alcohol.txt",delimter ...

  3. python解决物理问题_利用Python科学计算处理物理问题(和物理告个别)

    背景: 2019 年初由于尚未学习量子力学相关知识,所以处于自学阶段.浅显的学习了曾谨言的量子力学一卷和格里菲斯编写的量子力学教材.注重将量子力学的一些基本概念了解并理解.同时老师向我们推荐了 Qua ...

  4. python sobel算子_利用sobel算子计算图像的梯度(python+opencv)

    sobel算子计算的梯度图是带有方向的 代码实现如下: #coding=utf-8 import cv2 import numpy as np #利用sobel算子计算图像的梯度 img=cv2.im ...

  5. python怎么计算曲面的表面积_利用simpson积分公式计算曲面表面积

    利用 simpson 积分公式计算曲面表面积 夏军剑 ; 张新巍 ; 李维伟 [期刊名称] <科技资讯> [年 ( 卷 ), 期] 2014(012)008 [摘要] 二重积分的数值算法比 ...

  6. python支持函数式编程吗_利用Fn.py库在Python中进行函数式编程

    尽管Python事实上并不是一门纯函数式编程语言,但它本身是一门多范型语言,并给了你足够的自由利用函数式编程的便利.函数式风格有着各种理论与实际上的好处(你可以在Python的文档中找到这个列表): ...

  7. python speed为0但是速度过快_通过并发加速你的 python 程序

    本文翻译至 realpython 上题为 Speed Up Your Python Program With Concurrency 的教程,教程对怎么利用并发加速 python 程序分析非常全面到位 ...

  8. python自定义函数画图_利用Python绘图和可视化(长文慎入)

    Python有许多可视化工具,但是我主要讲解matplotlib(http://matplotlib.sourceforge.net).此外,还可以利用诸如d3.js(http://d3js.org/ ...

  9. python金融量化风险_利用 Python 进行量化投资分析 - 利率及风险资产的超额收益...

    本文是 利用 Python 进行量化投资分析 系列的第一篇文章,这个系列主要以 Python 作为工具,结果国内金融市场情况,及使用真实历史数据来实践一些基础的金融投资概念. 这篇文章主要讲述了真实利 ...

最新文章

  1. Docker入门六部曲——服务
  2. 更加安全的存取账户密码
  3. 什么是数据中心,它们是如何变化的?
  4. 【安全漏洞】ThinkPHP 3.2.3 漏洞复现
  5. qmoc文件_Qt中Q_OBJECT与生成的moc文件的作用
  6. 性能调优-硬盘方面,操作系统方面,文件系统方面
  7. 性能测试流程-各阶段的工作
  8. Java zset 应用_Java简单使用redis-zset实现排行榜
  9. 遗传算法python实例_Python遗传算法框架使用实例(一)使用Geatpy实现句子匹配
  10. VelocityTracker简单用法
  11. 本地文件共享到云服务器,Linux系统通过RDP上传文件到Windows云服务器
  12. 安卓导航车机root方法_车机测试 | 测试比亚迪e1车机系统
  13. 伊斯坦布尔之旅第一天:蓝色清真寺和圣索菲亚博物馆
  14. Macintosh30周年回顾视频 ---转自36kr
  15. 典型二阶系统的计算机仿真,二阶系统电路设计_RLC串联二阶电路实验报告
  16. Tomcat启动时报错:A child container failed during start解决方案-clean
  17. Adobe全家桶功能介绍
  18. Github上8个很棒的React项目
  19. 中国软件开发者(研究生)的人生规划(转自天涯虚拟社区)
  20. python3编写http代理服务器_HTTP代理服务器[Python]

热门文章

  1. PLM系统的技术现状和发展趋势
  2. 环回接口(Loopback Interface)【转】
  3. 我玩过的微信小程_已迁移
  4. 机器学习常见算法思想的面试宝典
  5. html实现文字移动的特效
  6. 杭电OJ 1047(C++)
  7. h3c trunk口改access_H3C交换机端口链路类型Trunk 端口配置指导
  8. Online Learning and Pricing with Reusable Resources: Linear Bandits with Sub-Exponential Rewards: Li
  9. C语言文件操作(1)
  10. Android开发中的Log打印日志