python异步io多文件_Python 异步 IO 性能又上一层楼
最近看源碼的過程中,發(fā)現(xiàn)了一個比較有意思的庫,aiomultiprocess,我認為他確實是 Python 升級到 3.8 之后一個特性的總結庫,包括靜態(tài)檢查和性能提升。
這個是作者提供的一個 IO 性能對比圖:
因為其場景主要是圖片上傳,所以性能對比僅供參考,從邏輯上面推理,按照作者的思路確實可以提升不少 IO 性能。
作者實際上結合了 Python 的三個特性:
multiprocess
asyncio
map/reduce
這三個庫其實都是很普通的庫,使用過 Python3 之后的版本,基本上都會接觸到,那為什么作者可以從這三個庫上面獲取到比較好的性能?主要是因為他在這三個庫的基礎上優(yōu)化了協(xié)程的調度方式。
優(yōu)化后的協(xié)程調度方式更加類似于 Golang 的調度方式,唯一的差異是少了搶占式調度的思路,僅僅使用了 RoundRobin 的方式,所以 IO 性能跟 Go 還有差距,但是肯定比 Nodejs 的性能要好一些。
協(xié)程、線程、進程之間的區(qū)別
這個問題對于剛接觸的人來說很復雜。先說進程和線程,進程和線程都是操作系統(tǒng)的概念,線程必須要在進程里面運行,另外進程是獨立的存儲空間,而線程的存儲空間是他所在進程的一部分,所以線程的開銷比進程小,線程的切換是邏輯上面的切換,但是進程的切換實際發(fā)生在物理 CPU 上面的;協(xié)程可以認為是比線程更加輕量的執(zhí)行單元,他使用了線程的存儲空間,協(xié)程之間通過 function 級別的 namespace 區(qū)分存儲單元。綜合來看他們三者最大的差異是在所在的運行時以及切換的開銷。
CPU 上面的 4 核 8 線程和我們說的線程是什么關系?4 核 8 線程的意思是每個 CPU 核上面有兩個獨立的線程工作單元,所以 4 個核心一共有 8 個線程,這 8 個線程可以看做是 8 條流水線,我們上文討論的線程、進程、協(xié)程全部都是操作系統(tǒng)邏輯上面的概念,與這個關系不大。
在一個 4 核 8 線程的機器上面,理論上可以同時運行 8 個進程。
如果進程數(shù)量大于 CPU 實際的線程數(shù)怎么辦?操作系統(tǒng)進程有很多,開機就有 100+,那么實際上我們的 CPU 線程數(shù)是有限的,這 100+ 的系統(tǒng)進程需要按照不同的優(yōu)先級由操作系統(tǒng)分時調度在不同的 CPU 線程上面工作。
如何理解并行和并發(fā)?并行和并發(fā)都是指事情同時發(fā)生,區(qū)別是...? emm 好像不是很好回答,唯一能夠準確描述的是并行是獨立事件,并發(fā)是關聯(lián)事件。舉個例子:一條四車道的路可以并排行駛 4 個車輛,但是如果這條路只有一車道的話,那么也可以行駛著四個車輛,區(qū)別是有的車輛需要排隊了,同時行駛的我們認為是并行,排隊走同一個車道的我們認為是并發(fā)。
當系統(tǒng)的進程數(shù)量 100 大于 CPU 的實際線程數(shù)量 8 時,那么系統(tǒng)并行數(shù)是 8,并發(fā)數(shù)量是 100。這種方法同樣適用于描述線程和協(xié)程。
Python 為什么慢?
Python 解釋器是單進程的。一個 Python 解釋器是一個單進程的應用,再加上 GIL 鎖,導致在進程里面不能并行執(zhí)行線程,不同的線程是分時使用進程所在的 CPU 執(zhí)行時間的。
因為 GIL 所以即使現(xiàn)在有充足的系統(tǒng)資源,那么 Request 1,Request 2,Request 3 也沒有辦法同時發(fā)生。
動態(tài)類型。動態(tài)類型讓 Python 寫起來非常的簡單,所有的類型默認都是對象,所以編寫代碼非常的簡單直接,但是執(zhí)行的時候,解釋器需要去推導類型,并動態(tài)檢查類型有沒有問題,所以導致運行時的任務比較重,相對于靜態(tài)編譯語言,速度跟不上。
協(xié)程的基本模型
協(xié)程實際上就是一個 function 級別的任務,分成兩類,一類跟 IO 有關系,一類跟 IO 沒有關系。Golang 的優(yōu)勢是能很好的處理計算和 IO 相關的協(xié)程,目前在 Python 里面勉強可以解決跟 IO 相關的協(xié)程。
IO 實際上是沒法避免的,比如磁盤訪問,網(wǎng)絡 IO 等,他一定會有一定的等待時間,所以無論怎么調度,都會有 IO 等待,為了解決這個問題,操作系統(tǒng)引入了不少的模型:select、poll、epoll 等。
那么基本上所有的編程語言都是利用了系統(tǒng)調用,也就是使用這些模型來解決問題,但是他們只可能比操作系統(tǒng)差,不能更好,因為有些編程語言誕生的比較早,大規(guī)模 IO 問題當時還沒遇到,說白了也就是那個時候沒有那么多用戶。
我使用 Python 語言大致經(jīng)歷了:進程 IO,線程 IO,協(xié)程 IO,協(xié)程池等,目前看到最優(yōu)的就是 facebook 的這個 aiomultiprocess。
IO 協(xié)程也就是把 IO 封裝成 function 級別的單元來調度,然后利用操作系統(tǒng)的 IO 調度模型,提升應用程序的性能,相對于進程和線程來說對于系統(tǒng)資源的利用更加合理,而且存儲開銷更加小,調度開銷也小很多。
epoll 基本模型
epoll 是目前主流的 IO 調度模型,調度性能最優(yōu)秀的其實是微軟的 IOCP,但是因為閉源,而且系統(tǒng)應用開發(fā)一般集中在 linux 所以對他也了解的不多。
epoll 實際上把 IO 等待問題集中交給操作系統(tǒng)處理,并要求操作系統(tǒng)出現(xiàn)我關心的事件的時候提醒我(比如某個文件描述符可以讀取,可以寫入,出現(xiàn)了錯誤等),把 IO 問題交給操作系統(tǒng)之后,應用程序就可以更加集中的去處理業(yè)務問題,而操作系統(tǒng)有更好的方式處理 IO,這就是站在了巨人的肩膀上。
Python 中的線程,進程,協(xié)程處理 IO 問題
Python 中使用進程處理問題相當于利用多核去等待 IO,相對于線程來說,使用了多核等待,線程是單核輪訓等待,使用多線程和多進程處理 IO 問題性能上面差異并不是很大,但是系統(tǒng)開銷很大,特別的進程的開銷還要大于線程,很容易導致操作系統(tǒng)卡頓。
協(xié)程因為開銷小,所以可以同時創(chuàng)建上萬的協(xié)程,所以其并發(fā)能力遠大于進程和線程,因為總需要等待 IO 時間,所以單進程內部不同的協(xié)程之間通過共享時間片的方式也是合理的。
AsyncIO 和協(xié)程之間的關系
AsyncIO 和協(xié)程之間并沒有什么關系,但是他們通常同時出現(xiàn),主要是 AsyncIO 需要依附于協(xié)程作為容器,普通的協(xié)程只是在發(fā)生 IO 等待的時候交出了執(zhí)行權限給其他協(xié)程,告訴其他人:我現(xiàn)在需要等待 IO 了,你先使用 CPU。但是在 AsyncIO 中,我們使用 epoll 配合 IO api,實際的調度是 epoll 來管理 IO,普通的協(xié)程還是利用 Python 應用程序管理 IO,所以本質上還是有很大差別的。
aiomultiprocess 為什么更快?
aiomultiprocess 將異步 IO 和多進程結合起來了,很好的利用了多核和異步 IO 的優(yōu)勢,前面說到 Python 是單進程的,所以即使有 AsyncIO 那么也并沒有充分利用操作系統(tǒng)的資源,相對來說還不是很快,但是結合了多進程之后,性能就可以線性增長了。
Golang 為什么更快?
既然充分了利用了多核,那么為什么還是比 Golang 慢?主要還是調度器的原因,Golang 是搶占式調度,目前 facebook 的這個版本主要是通過 RoundRobin 調度的,默認認為每一個任務和每一個 worker 的執(zhí)行時間是固定的,但是如果出現(xiàn)比較意外的情況,比如系統(tǒng)資源占用,就會導致進程出現(xiàn)假死,那么依附在進程隊列上面的任務也不能得到調度,為此作者增加了進程的生命之周期管理 TTL,執(zhí)行一定的任務之后,自毀并重新創(chuàng)建進程,這種方式可以緩解調度問題,但是粒度還是比較粗。
總結
aiomultiprocess 這個庫可以說是很棒的一個 IO 模型庫了,合理的利用多核來完成 IO 任務,相對于單獨使用線程,進程,異步 IO 來說,會有一個不錯的提升,可廣泛用于大規(guī)模的異步 IO 處理,比如發(fā)送郵件,上傳數(shù)據(jù)等。
總結
以上是生活随笔為你收集整理的python异步io多文件_Python 异步 IO 性能又上一层楼的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 传感器为什么在低量程偏差大_传感器复习
- 下一篇: php实现小说字典功能_四十章 PHP