平行运算

前言:

  编写Python程序时,我们可能会遭遇性能问题,即使优化了代码,程序也依然有可能运行的很慢,从而无法满足我们对执行速度的要求,目前的计算机,其cpu核心数越来越多,于是,我们可以考虑通过平行计算来提升性能,能不能把代码的总计算量分配到多个独立的任务之中,并在多个CPU核心上面同时运行这些任务呢?

  很遗憾,Python的全局解释器锁(GIL)使得我们没有办法用线程实现真正的平行计算,因此,上面那个想法行不通。另外一种常见的建议,是用C语言把程序中对性能要求较高的那部分代码,改为扩展模块,由于C语言更贴近硬件,所以运行的比Python快,一旦运行速度达到要求,我们自然就不用再考虑平行计算了,C语言扩展也可以启动并并行地运行多个原声线程,从而充分利用CPU的多个内核。Python中的C语言扩展API,有完备的文档可供查阅,这使得它成为解决性能问题的一个好办法。

  但是,用C语言重写代码,是有很大代价的,短小而易读的Python代码,会变成冗长而费解的C代码,在进行这样的移植时,必须进行大量的测试,确保移植过程中没有引入bug。然而问题在于:只把程序中的一小部分迁移到C,通常是不够的。一般来说,Python程序之所以执行得比较慢,并不是某个主要因素单独造成的,而是多个因素联合导致的,所以,要想充分利用C语言的硬件和线程优势,就必须把程序中的大量代码移植到C,而这样做,有大幅增加了测试量和风险。于是,我们应该思考一下:有没有一种更好的方式,只需要使用较少的Python代码,即可有效提升执行速度,并迅速解决复杂的计算问题。

  我们可以试着通过内置的concurrent.futures模块,来利用另外一个名叫multiprocessing的内置模块,从而实现这种需求,该做法会以子进程的形式,平行地运行多个解释器,从而令Python程序能够利用多核心CPU提升执行速度,由于子进程与主解释器相分离,所以它们的全局解释器锁也是相互独立的,每个紫禁城都可以完整地利用一个CPU内核,而且这些自经常,都与主进程之间有着联系,通过这条联系渠道,紫禁城可以接收主进程发过来的指令,并把计算结果返回给主进程

程序运算:

  编写运算量很大的Python程序,查找两数最大公约数,用三种不同的方式进行对比

① 单线程

代码:

# 单线程
import time def gcd(pair):
a,b = pair
low = min(a,b)
for i in range(low,0,-1):
if a % i == 0 and b % i == 0:
return i numbers = [(89937224,53452411),(97432894,43939284),(95938272,94910833),
(7398473,47382942),(85938272,90493759)]
start = time.time()
results = list(map(gcd,numbers))
end = time.time()
print('Took %.3f secondes'%(end-start))

执行结果:

Took 22.083 secondes

多线程

代码:

# 多线程
import time
from concurrent.futures import ThreadPoolExecutor def gcd(pair):
a,b = pair
low = min(a,b)
for i in range(low,0,-1):
if a % i == 0 and b % i == 0:
return i numbers = [(89937224,53452411),(97432894,43939284),(95938272,94910833),
(7398473,47382942),(85938272,90493759)]
start = time.time()
pool = ThreadPoolExecutor(max_workers=2)
results = list(pool.map(gcd,numbers))
end = time.time()
print('Took %.3f secondes'%(end-start))

执行结果:

Took 25.338 secondes

注:用多条Python现场来改善上述程序,是没有效果的,因为全局解释器锁(GIL)使得Python无法在多个CPU核心上面平行地运行这些线程。线程启动的时候,是有一定开销的,与线程池进行通信,也会有开销,所以上面这个程序运行的比单线程版本还要满

多进程(只能linux下运行)

我们只需要改动一行代码,就可以提升整个程序的速度,把ThreadPoolExecutor换成concurrent.futures模块里的ProcessPoolExecutor,程序的速度就上去了

代码:

import time
from concurrent.futures import ProcessPoolExecutor
from multiprocessing import cpu_count def gcd(pair):
a,b = pair
low = min(a,b)
for i in range(low,0,-1):
if a % i == 0 and b % i == 0:
return i numbers = [(89937224,53452411),(97432894,43939284),(95938272,94910833),
(7398473,47382942),(85938272,90493759)]
start = time.time()
pool = ProcessPoolExecutor(max_workers=cpu_count()) # 四核
results = list(pool.map(gcd,numbers))
end = time.time()
print('Took %.3f secondes'%(end-start))

执行结果:

Took 6.816 secondes

注:果然比前面两个版本的程序执行速度快了很多

总结:

ProcessPoolExecutor类利用由multiprocessing模块所提供的底层机制,来逐步完成下列操作:

  • 把numbers列表中的每一项输入数据都传给map。
  • 用pickle模块对数据进行序列化,将其变成二进制形式。
  • 通过本地套接字(local socket),将序列化之后的数据从主解释器所在的进程,发到子解释器所在的进程。
  • 接下来,在子进程中,用pickle对二进制数据进行反序列化操作,将其还原为Python对象。
  • 引入包含gcd函数的那个Python模块。
  • 各条子进程平行地针对各自的输入数据,来运行gcd函数。
  • 对运行结果进行序列化操作,将其转变为字节。
  • 将这些字节通过socket负责到主进程之中。
  • 主进程对这些字节执行反序列话操作,将其还原为Python对象。
  • 最后,把每条子进程所求出的计算结果合并到一份列表之中,并返回给调用者

  从编程者的角度看,上面的这些步骤,似乎是比较简单的,但实际上,为了实现平行计算,mutiprocessing模块和ProcessPoolExecutor类在幕后做了大量的工作,如果改用其他编程语言来写,那么开发者只需要用一把同步锁或一项原子操作,就可以把线程之间的同学过程协调好,而在Python语言中,我们却必须使用开销较高的multiprocessing模块,mutiproocessing的开销之所以比较大,原因在于:主进程和子进程之间,必须进行序列化和反序列化操作,而程序中的大量开销,正式由这些操作所引发的。

  对于某些较为孤立,且数据利用率较高的任务来说,这套方案非常合适。所谓孤立,是指待运行的函数不需要与程序中的其他部分共享状态。所谓利用率高,是指只需要在主进程和紫禁城之间传递一部分数据,就能完成大量的运算。本例中的最大公约数算法,满足这两个条件,其他的一些类似数学算法,也可以通过这套方案实现平行计算。

  

Python开发【笔记】:concurrent.futures 平行运算的更多相关文章

  1. python 全栈开发,Day42(Thread类的其他方法,同步锁,死锁与递归锁,信号量,事件,条件,定时器,队列,Python标准模块--concurrent.futures)

    昨日内容回顾 线程什么是线程?线程是cpu调度的最小单位进程是资源分配的最小单位 进程和线程是什么关系? 线程是在进程中的 一个执行单位 多进程 本质上开启的这个进程里就有一个线程 多线程 单纯的在当 ...

  2. python全栈开发,Day42(Thread类的其他方法,同步锁,死锁与递归锁,信号量,事件,条件,定时器,队列,Python标准模块--concurrent.futures)

    昨日内容回顾 线程 什么是线程? 线程是cpu调度的最小单位 进程是资源分配的最小单位 进程和线程是什么关系? 线程是在进程中的一个执行单位 多进程 本质上开启的这个进程里就有一个线程 多线程 单纯的 ...

  3. Thread类的其他方法,同步锁,死锁与递归锁,信号量,事件,条件,定时器,队列,Python标准模块--concurrent.futures

    参考博客: https://www.cnblogs.com/xiao987334176/p/9046028.html 线程简述 什么是线程?线程是cpu调度的最小单位进程是资源分配的最小单位 进程和线 ...

  4. python开发笔记-通过xml快捷获取数据

    今天在做下python开发笔记之如何通过xml快捷获取数据,下面以调取nltk语料库为例: import nltk nltk.download() showing info https://raw.g ...

  5. Python标准模块--concurrent.futures

    1 模块简介 concurrent.futures模块是在Python3.2中添加的.根据Python的官方文档,concurrent.futures模块提供给开发者一个执行异步调用的高级接口.con ...

  6. 在python中使用concurrent.futures实现进程池和线程池

    #!/usr/bin/env python # -*- coding: utf-8 -*- import concurrent.futures import time number_list = [1 ...

  7. Python标准模块--concurrent.futures(进程池,线程池)

    python为我们提供的标准模块concurrent.futures里面有ThreadPoolExecutor(线程池)和ProcessPoolExecutor(进程池)两个模块. 在这个模块里他们俩 ...

  8. python并发之concurrent.futures

    concurrent:并发 Python标准库为我们提供了threading和multiprocessing模块编写相应的多线程/多进程代码.从Python3.2开始,标准库为我们提供了concurr ...

  9. Python标准模块--concurrent.futures 进程池线程池终极用法

    concurrent.futures 这个模块是异步调用的机制concurrent.futures 提交任务都是用submitfor + submit 多个任务的提交shutdown 是等效于Pool ...

随机推荐

  1. python cython 模块(1)

    python 是一门动态类型的语音,其开发速度比C,C++等静态语言块, 但是速度慢很多, 而cython 通过混合C和python 的语法,可以提高python代码的运行速度 1) 安装cython ...

  2. 利用kseq.h parse fasta/fastq 文件

    在分析中经常需要统计fasta/fastq文件的序列数和碱基数, 但是没有找到一些专门做这件事的小工具,可能是这个功能太简单了: 之前用自己写的perl的脚本统计这些信息, 当fastq文件非常大时, ...

  3. struts2将数据通过Json格式显示于EasyUI-datagrid数据表格

    1.搭建ssh开发环境 2.写好Dao.service等方法 3.建立DTO数据传输对象: package com.beichende.sshwork.user.web.dto; import jav ...

  4. 【Java集合的详细研究9】Java堆栈(stack)的使用方法

    栈是一种常用的数据结构,栈只允许访问栈顶的元素,栈就像一个杯子,每次都只能取杯子顶上的东西,而对于栈就只能每次访问它的栈顶元素,从而可以达到保护栈顶元素以下的其他元素.”先进后出”或”后进先出”就是栈 ...

  5. JavaSE(十)之反射

    开始接触的时候可能大家都会很模糊到底什么是反射,大家都以为这个东西不重要,其实很重要的,几乎所有的框架都要用到反射,增加灵活度.到了后面几乎动不动就要用到反射. 首先我们先来认识一下对象 学生---- ...

  6. 记录一下我的GDB配置

    一:为了更好的在GDB中显示STL容器.我们首先要下载一个python脚本 PS:要确定你所安装的GDB能够运行python脚本 cd ~ mkdir .gdb cd .gdb svn co svn: ...

  7. write solid code 零散(原文)

    整理下目录,看了这个文件,幸好未删除. 以下是<write solid code>中的原文摘录. 1.How could I have prevented this bug? 2.How ...

  8. 【转】Internet与Intranet区别

    提起Internet,大家都知道它是一个蓬勃发展的国际互联网. 而Intranet则是近两年才发展起来的新事物,通常被称作企业内部网. Internet是一组全球范围内信息资源的名字.这些资源非常巨大 ...

  9. css之鼠标cursor

    <html> <body> <p>请把鼠标移动到单词上,可以看到鼠标指针发生变化:</p> <span style="cursor:au ...

  10. 如何调用别人发布的WebService程序

    这篇经验会告诉我们如何调用别人发布的WebService,并且需要注意的事项.现在就拿获取天气预报的接口举例,因为文中不允许有链接,所以在下文图中有WebService链接的地址. 工具/原料   V ...