IO 密集型应用

IO 密集型应用CPU等待IO时间远大于CPU 自身运行时间,太浪费;常见的 IO 密集型业务包括:浏览器交互、磁盘请求、网络爬虫、数据库请求等

image.png

Python 世界对于 IO 密集型场景的并发提升有 3 种方法:多进程、多线程、异步 IO(asyncio);理论上讲asyncio是性能最高的,原因如下:

1.进程、线程会有CPU上下文切换

2.进程、线程需要内核态和用户态的交互,性能开销大;而协程对内核透明的,只在用户态运行

3.进程、线程并不可以无限创建,最佳实践一般是 CPU*2;而协程并发能力强,并发上限理论上取决于操作系统IO多路复用(Linux下是 epoll)可注册的文件描述符的极限

那asyncio的实际表现是否如理论上那么强,到底强多少呢?我构建了如下测试场景:

访问500台 DB,并sleep 100ms模拟业务查询

方法 1;顺序串行一台台执行

方法 2:多进程

方法 3:多线程

方法 4:asyncio

方法 5:asyncio+uvloop

最后的asyncio+uvloop和官方asyncio 最大不同是用 Cython+libuv 重新实现了asyncio 的事件循环(event loop)部分,官方测试性能是 node.js的 2 倍,持平 golang。

以下测试代码需要 Pyhton3.7+:

顺序串行一台台执行

#!/usr/bin/env python3

# -*- coding: utf-8 -*-

import records

user=xx

pass=xx

port=xx

hosts= [....] # 500台 db列表

def query(host):

conn = records.Database(

f'mysql+pymysql://{user}:{pass}@{host}:{port}/mysql?charset=utf8mb4')

rows = conn.query('select sleep(0.1);')

print(rows[0])

def main():

for h in hosts:

query(h)

# main entrance

if __name__ == '__main__':

main()

多进程

#!/usr/bin/env python3

# -*- coding: utf-8 -*-

from concurrent import futures

import records

user=xx

pass=xx

port=xx

hosts= [....] # 500台 db列表

def query(host):

conn = records.Database(

f'mysql+pymysql://{user}:{pass}@{host}:{port}/mysql?charset=utf8mb4')

rows = conn.query('select sleep(0.1);')

print(rows[0])

def main():

with futures.ProcessPoolExecutor() as executor:

for future in executor.map(query,hosts):

pass

# main entrance

if __name__ == '__main__':

main()

多线程

#!/usr/bin/env python3

# -*- coding: utf-8 -*-

from concurrent import futures

import records

user=xx

pass=xx

port=xx

hosts= [....] # 500台 db列表

def query(host):

conn = records.Database(

f'mysql+pymysql://{user}:{pass}@{host}:{port}/mysql?charset=utf8mb4')

rows = conn.query('select sleep(0.1);')

print(rows[0])

def main():

with futures.ThreadPoolExecutor() as executor:

for future in executor.map(query,hosts):

pass

# main entrance

if __name__ == '__main__':

main()

asyncio

#!/usr/bin/env python3

# -*- coding: utf-8 -*-

import asyncio

from databases import Database

user=xx

pass=xx

port=xx

hosts= [....] # 500台 db列表

async def query(host):

DATABASE_URL = f'mysql+pymysql://{user}:{pass}@{host}:{port}/mysql?charset=utf8mb4'

async with Database(DATABASE_URL) as database:

query = 'select sleep(0.1);'

rows = await database.fetch_all(query=query)

print(rows[0])

async def main():

tasks = [asyncio.create_task(query(host)) for host in hosts]

await asyncio.gather(*tasks)

# main entrance

if __name__ == '__main__':

asyncio.run(main())

asyncio+uvloop

#!/usr/bin/env python3

# -*- coding: utf-8 -*-

import asyncio

import uvloop

from databases import Database

user=xx

pass=xx

port=xx

hosts= [....] # 500台 db列表

async def query(host):

DATABASE_URL = f'mysql+pymysql://{user}:{pass}@{host}:{port}/mysql?charset=utf8mb4'

async with Database(DATABASE_URL) as database:

query = 'select sleep(0.1);'

rows = await database.fetch_all(query=query)

print(rows[0])

async def main():

tasks = [asyncio.create_task(query(host)) for host in hosts]

await asyncio.gather(*tasks)

# main entrance

if __name__ == '__main__':

uvloop.install()

asyncio.run(main())

运行时间对比

方式

运行时间

串行

1m7.745s

多进程

2.932s

多线程

4.813s

asyncio

1.068s

asyncio+uvloop

0.750s

可以看出: 无论多进程、多进程还是asyncio都能大幅提升IO 密集型场景下的并发,但asyncio+uvloop性能最高,运行时间只有原始串行运行时间的 1/90,相差快 2 个数量级了!

内存占用对比

串行

image.png

多进程

image.png

多线程

image.png

asyncio

image.png

asyncio+uvloop

image.png

可以看出asyncio+uvloop内存占用表现仍然最优,只有 60M;而多进程占用多达 1.4G,果然创建进程是个十分重的操作~

总结

asyncio 无论运行时间还是内存占用都远优于多进程、多线程,配合 uvloop 性能还能进一步提升,在 IO 密集型业务中可以优先使用 asyncio。

python异步和多线程_Python 异步 IO(asyncio)、多进程、多线程性能对比相关推荐

  1. python网络编程实战_Python 异步网络编程实战

    近年来 Python 的发展的非常迅速,"简单"."高效"是 Python 吸引人的一大特色.在国内 Python 开发需求越来越大,Python 具有丰富强大 ...

  2. python下载vip素材_Python素材下载爬虫,多线程rar下载爬虫

    一个多线程素材下载爬虫,实现多线程素材下载,包含素材包rar,素材图及素材描述,应用了经典的生产者与消费者模式,不过数据没有下载全,容易出现卡死的问题,期待后期能够解决相关问题,可以算是一个半成品,供 ...

  3. python 异步文件读写_Python中使用asyncio 封装文件读写

    前言 和网络 IO 一样,文件读写同样是一个费事的操作. 默认情况下,Python 使用的是系统的阻塞读写.这意味着在 asyncio 中如果调用了 f = file('xx') f.read() 会 ...

  4. python异步处理请求_Python 异步请求

    1. 正常下载漫画,requests 阻塞式. import requests, bs4, time import asyncio, aiohttp def get_image_urls(url): ...

  5. python异步和进程_Python异步缓冲区和进程d

    您希望能够尽快处理进入Reader的所有数据,但也不能让多个线程/进程尝试并行处理这些数据:这就是以前使用执行器遇到竞争条件的原因.相反,您应该启动一个可以处理所有数据包数据的工作进程,一次一个,使用 ...

  6. python异步处理请求_python异步处理HTTP请求

    我需要为django查询集的每个条目生成一个PDF报告.会有3万到4万个条目.在 PDF是通过外部API生成的.由于当前是按需生成的,因此这是通过HTTP请求/响应同步处理的. 对于这个任务,这是不同 ...

  7. python的异步网络编程_python异步网络编程怎么使socket关闭之后立即执行一段代码?...

    import socket from _socket import getdefaulttimeout class MySocket(socket.socket): """ ...

  8. python queue 多线程_Python如何实现并行的多线程?

    如果大家在学习中遇到困难,想找一个python学习交流环境,可以点击下方加入我们一起学习,欢迎零基础和大佬加入正在跳转​jq.qq.com 有关Python问题都可以给我留言喔 接下来说正事,如果待处 ...

  9. python中线程里面多线程_Python中的线程和多线程是什么

    一.线程的概念 一个进程里面至少有一个控制线程,进程的概念只是一种抽象的概念,真正在CPU上面调度的是进程里面的线程,就好比真正在地铁这个进程里面工作的实际上是地铁里面的线程,北京地铁里面至少要有一个 ...

最新文章

  1. String、StringBuffer与StringBuilder之间区别 (转载)
  2. 大数据分析中国冬季重度雾霾的成因(一)
  3. 代码管理平台之svn
  4. 用Android Studio调试Framework层代码
  5. Hibernate一级缓存常用API
  6. Bootstrap FileInput(文件上传)中文API整理
  7. 我的iOS学习历程 - UISegmentedControl
  8. mac os nginx php mysql
  9. SQL Server 2008 Service Broker
  10. pcie转sata3硬盘不启动_XPS 笔记本: 排除对 BIOS 默认设置的更改导致无法开机自检/无引导/硬盘或未检测到 SDD 问题...
  11. 深度学习(8)-NLP(词嵌入,嵌入矩阵,词向量,情绪分类,偏见问题)
  12. The Elder(树形dp 斜率优化)
  13. 各种标点符号的英文怎么说
  14. python的idle怎么运行_python中的idle是如何运行的
  15. No row with the given identifier exists 的原因和解决方法
  16. error怎么开机 fan_笔记本开机显示fan error怎么解决?
  17. MVP登录 二维码扫描
  18. iPone应用开发 UIView 常用属性和方法
  19. 树莓派 | 01 无显示器安装并通过wifi、ssh无线连接学习教程记录
  20. JavaScript——this关键字

热门文章

  1. WordPress的Kyma plugin HTML发送的connect请求是怎么投递到PHP的
  2. 研究SAP service order status存储字段
  3. 走进rxjs的世界 - 万物皆流 - Everything is a stream
  4. SAP Fiori customer report的学习
  5. SAP S4CRM 1811 服务订单API介绍
  6. mysql 事务 myisam,InnoDB和MyISAM的差别(mysql事务处理)
  7. 指的是什么意思_扈三娘的外号一丈青,到底指的是什么意思?
  8. UE4学习-请求的操作需要提升
  9. 数据仓库工具箱:维度建模权威指南3
  10. c++ 私有内部类_Java内部类新解,你没有见过的船新版本