python异步和多线程_Python 异步 IO(asyncio)、多进程、多线程性能对比
IO 密集型應用
IO 密集型應用CPU等待IO時間遠大于CPU 自身運行時間,太浪費;常見的 IO 密集型業(yè)務包括:瀏覽器交互、磁盤請求、網絡爬蟲、數據庫請求等
image.png
Python 世界對于 IO 密集型場景的并發(fā)提升有 3 種方法:多進程、多線程、異步 IO(asyncio);理論上講asyncio是性能最高的,原因如下:
1.進程、線程會有CPU上下文切換
2.進程、線程需要內核態(tài)和用戶態(tài)的交互,性能開銷大;而協(xié)程對內核透明的,只在用戶態(tài)運行
3.進程、線程并不可以無限創(chuàng)建,最佳實踐一般是 CPU*2;而協(xié)程并發(fā)能力強,并發(fā)上限理論上取決于操作系統(tǒng)IO多路復用(Linux下是 epoll)可注冊的文件描述符的極限
那asyncio的實際表現是否如理論上那么強,到底強多少呢?我構建了如下測試場景:
訪問500臺 DB,并sleep 100ms模擬業(yè)務查詢
方法 1;順序串行一臺臺執(zhí)行
方法 2:多進程
方法 3:多線程
方法 4:asyncio
方法 5:asyncio+uvloop
最后的asyncio+uvloop和官方asyncio 最大不同是用 Cython+libuv 重新實現了asyncio 的事件循環(huán)(event loop)部分,官方測試性能是 node.js的 2 倍,持平 golang。
以下測試代碼需要 Pyhton3.7+:
順序串行一臺臺執(zhí)行
#!/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 密集型場景下的并發(fā),但asyncio+uvloop性能最高,運行時間只有原始串行運行時間的 1/90,相差快 2 個數量級了!
內存占用對比
串行
image.png
多進程
image.png
多線程
image.png
asyncio
image.png
asyncio+uvloop
image.png
可以看出asyncio+uvloop內存占用表現仍然最優(yōu),只有 60M;而多進程占用多達 1.4G,果然創(chuàng)建進程是個十分重的操作~
總結
asyncio 無論運行時間還是內存占用都遠優(yōu)于多進程、多線程,配合 uvloop 性能還能進一步提升,在 IO 密集型業(yè)務中可以優(yōu)先使用 asyncio。
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的python异步和多线程_Python 异步 IO(asyncio)、多进程、多线程性能对比的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: python 线程同步_Python 线
 - 下一篇: volatile的应用