龙空技术网

Python 异步 IO(asyncio)、多进程、多线程性能对比

闪念基因 11861

前言:

现在咱们对“python异步和多线程区别”大约比较关切,你们都想要学习一些“python异步和多线程区别”的相关资讯。那么小编同时在网络上汇集了一些有关“python异步和多线程区别””的相关知识,希望兄弟们能喜欢,你们一起来学习一下吧!

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

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 recordsuser=xxpass=xxport=xxhosts= [....] # 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 entranceif __name__ == '__main__': main()

多进程

#!/usr/bin/env python3# -*- coding: utf-8 -*-from concurrent import futuresimport recordsuser=xxpass=xxport=xxhosts= [....] # 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 entranceif __name__ == '__main__': main()

多线程

#!/usr/bin/env python3# -*- coding: utf-8 -*-from concurrent import futuresimport recordsuser=xxpass=xxport=xxhosts= [....] # 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 entranceif __name__ == '__main__': main()

asyncio

#!/usr/bin/env python3# -*- coding: utf-8 -*-import asynciofrom databases import Database user=xxpass=xxport=xxhosts= [....] # 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 entranceif __name__ == '__main__': asyncio.run(main())

asyncio+uvloop

#!/usr/bin/env python3# -*- coding: utf-8 -*-import asyncioimport uvloopfrom databases import Database user=xxpass=xxport=xxhosts= [....] # 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 entranceif __name__ == '__main__': uvloop.install() asyncio.run(main())

运行时间对比

方式运行时间串行1m7.745s多进程2.932s多线程4.813sasyncio1.068sasyncio+uvloop0.750s

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

内存占用对比 串行

多进程

多线程

asyncio

asyncio+uvloop

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

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

标签: #python异步和多线程区别 #python异步与多线程 区别 #python 异步线程 #python多线程异步并发