Python 多进程、多线程效率对比


Posted in Python onNovember 19, 2020

Python 界有条不成文的准则: 计算密集型任务适合多进程,IO 密集型任务适合多线程。本篇来作个比较。

通常来说多线程相对于多进程有优势,因为创建一个进程开销比较大,然而因为在 python 中有 GIL 这把大锁的存在,导致执行计算密集型任务时多线程实际只能是单线程。而且由于线程之间切换的开销导致多线程往往比实际的单线程还要慢,所以在 python 中计算密集型任务通常使用多进程,因为各个进程有各自独立的 GIL,互不干扰。

而在 IO 密集型任务中,CPU 时常处于等待状态,操作系统需要频繁与外界环境进行交互,如读写文件,在网络间通信等。在这期间 GIL 会被释放,因而就可以使用真正的多线程。

以上是理论,下面做一个简单的模拟测试: 大量计算用 math.sin() + math.cos() 来代替,IO 密集型用 time.sleep() 来模拟。 在 Python 中有多种方式可以实现多进程和多线程,这里一并纳入看看是否有效率差异:

  1. 多进程: joblib.multiprocessing, multiprocessing.Pool, multiprocessing.apply_async, concurrent.futures.ProcessPoolExecutor
  2. 多线程: joblib.threading, threading.Thread, concurrent.futures.ThreadPoolExecutor
from multiprocessing import Pool
from threading import Thread
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
import time, os, math
from joblib import Parallel, delayed, parallel_backend


def f_IO(a): # IO 密集型
 time.sleep(5)

def f_compute(a): # 计算密集型
 for _ in range(int(1e7)):
  math.sin(40) + math.cos(40)
 return

def normal(sub_f):
 for i in range(6):
  sub_f(i)
 return

def joblib_process(sub_f):
 with parallel_backend("multiprocessing", n_jobs=6):
  res = Parallel()(delayed(sub_f)(j) for j in range(6))
 return


def joblib_thread(sub_f):
 with parallel_backend('threading', n_jobs=6):
  res = Parallel()(delayed(sub_f)(j) for j in range(6))
 return

def mp(sub_f):
 with Pool(processes=6) as p:
  res = p.map(sub_f, list(range(6)))
 return

def asy(sub_f):
 with Pool(processes=6) as p:
  result = []
  for j in range(6):
   a = p.apply_async(sub_f, args=(j,))
   result.append(a)
  res = [j.get() for j in result]

def thread(sub_f):
 threads = []
 for j in range(6):
  t = Thread(target=sub_f, args=(j,))
  threads.append(t)
  t.start()
 for t in threads:
  t.join()

def thread_pool(sub_f):
 with ThreadPoolExecutor(max_workers=6) as executor:
  res = [executor.submit(sub_f, j) for j in range(6)]

def process_pool(sub_f):
 with ProcessPoolExecutor(max_workers=6) as executor:
  res = executor.map(sub_f, list(range(6)))

def showtime(f, sub_f, name):
 start_time = time.time()
 f(sub_f)
 print("{} time: {:.4f}s".format(name, time.time() - start_time))

def main(sub_f):
 showtime(normal, sub_f, "normal")
 print()
 print("------ 多进程 ------")
 showtime(joblib_process, sub_f, "joblib multiprocess")
 showtime(mp, sub_f, "pool")
 showtime(asy, sub_f, "async")
 showtime(process_pool, sub_f, "process_pool")
 print()
 print("----- 多线程 -----")
 showtime(joblib_thread, sub_f, "joblib thread")
 showtime(thread, sub_f, "thread")
 showtime(thread_pool, sub_f, "thread_pool")


if __name__ == "__main__":
 print("----- 计算密集型 -----")
 sub_f = f_compute
 main(sub_f)
 print()
 print("----- IO 密集型 -----")
 sub_f = f_IO
 main(sub_f)

结果:

----- 计算密集型 -----
normal time: 15.1212s

------ 多进程 ------
joblib multiprocess time: 8.2421s
pool time: 8.5439s
async time: 8.3229s
process_pool time: 8.1722s

----- 多线程 -----
joblib thread time: 21.5191s
thread time: 21.3865s
thread_pool time: 22.5104s



----- IO 密集型 -----
normal time: 30.0305s

------ 多进程 ------
joblib multiprocess time: 5.0345s
pool time: 5.0188s
async time: 5.0256s
process_pool time: 5.0263s

----- 多线程 -----
joblib thread time: 5.0142s
thread time: 5.0055s
thread_pool time: 5.0064s

上面每一方法都统一创建6个进程/线程,结果是计算密集型任务中速度:多进程 > 单进程/线程 > 多线程, IO 密集型任务速度: 多线程 > 多进程 > 单进程/线程。

以上就是Python 多进程、多线程效率比较的详细内容,更多关于Python 多进程、多线程的资料请关注三水点靠木其它相关文章!

Python 相关文章推荐
Python的Django框架中if标签的相关使用
Jul 15 Python
django主动抛出403异常的方法详解
Jan 04 Python
python 使用plt画图,去除图片四周的白边方法
Jul 09 Python
解决python flask中config配置管理的问题
Jul 26 Python
Django 迁移、操作数据库的方法
Aug 02 Python
python sklearn包——混淆矩阵、分类报告等自动生成方式
Feb 28 Python
使用jupyter notebook运行python和R的步骤
Aug 13 Python
python如何实现图片压缩
Sep 11 Python
python 读取yaml文件的两种方法(在unittest中使用)
Dec 01 Python
Python爬虫之爬取最新更新的小说网站
May 06 Python
使用Python开发冰球小游戏
Apr 30 Python
numpy array找出符合条件的数并赋值的示例代码
Jun 01 Python
Python导入父文件夹中模块并读取当前文件夹内的资源
Nov 19 #Python
Pytorch实验常用代码段汇总
Nov 19 #Python
Ubuntu配置Pytorch on Graph (PoG)环境过程图解
Nov 19 #Python
python基于pygame实现飞机大作战小游戏
Nov 19 #Python
Python numpy大矩阵运算内存不足如何解决
Nov 19 #Python
python3 os进行嵌套操作的实例讲解
Nov 19 #Python
如何创建一个Flask项目并进行简单配置
Nov 18 #Python
You might like
给apache2.2加上mod_encoding模块後 php5.2.0 处理url出现bug
2007/04/12 PHP
PHP的array_diff()函数在处理大数组时的效率问题
2011/11/27 PHP
php 函数中使用static的说明
2012/06/01 PHP
PHP弹出提示框并跳转到新页面即重定向到新页面
2014/01/24 PHP
yii2实现分页,带搜索的分页功能示例
2017/01/07 PHP
php语言注释,单行注释和多行注释
2018/01/21 PHP
PHP使用标准库spl实现的观察者模式示例
2018/08/04 PHP
php定期拉取数据对比方法实例
2019/09/22 PHP
JQuery textlimit 显示用户输入的字符数 限制用户输入的字符数
2009/05/14 Javascript
jQuery页面图片伴随滚动条逐渐显示的小例子
2013/03/21 Javascript
VS2008中使用JavaScript调用WebServices
2014/12/18 Javascript
JavaScript中的依赖注入详解
2015/03/18 Javascript
jquery动态增加删减表格行特效
2015/11/20 Javascript
JavaScript截取指定长度字符串点击可以展开全部代码
2015/12/04 Javascript
通过原生JS实现为元素添加事件的方法
2016/11/23 Javascript
基于bootstrap写的一点localStorage本地储存
2017/11/21 Javascript
以v-model与promise两种方式实现vue弹窗组件
2018/05/21 Javascript
详解多页应用 Webpack4 配置优化与踩坑记录
2018/10/16 Javascript
微信小程序传值以及获取值方法的详解
2019/04/29 Javascript
微信小程序前端promise封装代码实例
2019/08/24 Javascript
vue实现打地鼠小游戏
2020/08/21 Javascript
python3编码问题汇总
2016/09/06 Python
Python多线程经典问题之乘客做公交车算法实例
2017/03/22 Python
JSON Web Tokens的实现原理
2017/04/02 Python
python flask实现分页的示例代码
2018/08/02 Python
python pyheatmap包绘制热力图
2018/11/09 Python
python实现简单名片管理系统
2018/11/30 Python
解决在Python编辑器pycharm中程序run正常debug错误的问题
2019/01/17 Python
Python 中的 global 标识对变量作用域的影响
2019/08/12 Python
Python3使用xml.dom.minidom和xml.etree模块儿解析xml文件封装函数的方法
2019/09/23 Python
pytorch中的transforms模块实例详解
2019/12/31 Python
virtualenv介绍及简明教程
2020/06/23 Python
生物科学专业个人求职信范文
2013/12/05 职场文书
应急管理培训方案
2014/06/12 职场文书
安全温馨提示语大全
2015/07/14 职场文书
学习委员竞选稿
2015/11/20 职场文书