併發實現方案的效能比較

2021-08-16 18:36:09 字數 4289 閱讀 9077

偶然看到erlang vs. stackless python: a first benchmark,對erlang和stackless python的併發處理效能進行了實驗比較,基本結論認為二者有比較相近的效能。我看完產生的問題是,stackless python與python的其他併發實現機制效能又會有多大區別呢,比如執行緒和程序。因此我採用與這篇文章相同的辦法來對stackless python、普通python的thread模組、普通python的threading模組、普通python的processing模組這四種併發實現方案進行了效能實驗,並將實驗過程和基本結果記錄在這裡。

後來看到了基於greenlet實現的高效能網路框架eventlet,因而更新了實驗方案,將greenlet也加入了比較,雖然greenlet並非是一種真正意義上的併發處理,而是在單個執行緒下對程式塊進行切換輪流執行。

實驗方案與erlang vs. stackless python: a first benchmark是相同的,用每種方案分別給出如下問題的實現,記錄完成整個處理過程的總時間來作為評判效能的依據:

由n個節點組成乙個環狀網路,在上面傳送共m個訊息。

將每個訊息(共m個),逐個傳送給1號節點。

第1到n-1號節點在接收到訊息後,都**給下一號節點。

第n號節點每次收到訊息後,不再繼續**。

當m個訊息都從1號逐個到達第n號節點時,認為全部處理結束。

macbook pro 3,1上的vmware fusion 1.0虛擬機器中,注意這裡給虛擬機器只啟用了cpu的單個核心:

vmware fusion 1.0下的debian etch:

各方案的實現**見後文。實驗時使用time指令記錄每次執行的總時間,選用的都是不做任何輸出的no_io實現(python的print指令還是挺耗資源的,如果不注釋掉十有**得影響測試結果),每次執行時設定n=300,m=10000(erlang vs. stackless python: a first benchmark文章中認為n可以設定為300,m則可以取10000到90000之間的數值分別進行測試)。

real0m1.651s

user0m1.628s

sys0m0.020s

即使將m擴大到30000,實驗結果仍然很突出:

real0m4.749s

user0m4.716s

sys0m0.028s

real1m13.009s

user0m2.476s

sys0m59.028s

不太穩定,有時候這樣:

real1m9.222s

user0m34.418s

sys0m34.622s

也有時這樣:

real2m14.016s

user0m6.644s

sys2m7.260s

real3m43.539s

user0m15.345s

sys3m27.953s

real0m9.225s

user0m0.644s

sys0m8.581s

注意!eventlet 的這個實驗結果是後來增補的,硬體平台沒變,但是是直接在 osx 自帶 python 2.5 環境下執行出來的,同時系統中還有 firefox 等很多程式也在爭奪系統資源。因此只能作為大致參考,不能與其他幾組資料作直接對比。(其中 eventlet 的版本是 0.9.5)

real    0m21.610s

user 0m20.713s

sys 0m0.215s

毫無疑問,stackless python幾乎有匪夷所思的併發效能,比其他方案快上幾十倍,而且借助stackless python提供的channel機制,實現也相當簡單。也許這個結果向我們部分揭示了沈仙人基於stackless python實現的eurasia3能夠提供相當於c語言效果的恐怖併發效能的原因。

從道理上來講,thread模組似乎應該和threading提供基本相同的效能,畢竟threading只是對thread的一種封裝嘛,後台機制應該是一致的。或許threading由於本身類例項維護方面的開銷,應該會比直接用thread慢一點。從實驗結果來看,二者效能也確實差不多。只是不大明白為何threading方案的測試結果不是很穩定,即使對其他方案的測試執行多次,誤差也不會像threading這麼飄。從**實現體驗來說,用threading配合queue比直接用thread實在是輕鬆太多了,並且出錯的機會也要少很多。

processing模組給出的程序方案大致比thread執行緒要慢一倍,並且這是在我特意調整虛擬機器給它預備了足夠空閒記憶體、避免使用交換分割槽的情況下取得的(特意分給虛擬機器700多m記憶體就是為了這個)。而其他方案僅僅占用數m記憶體,完全無需特意調大可用記憶體總量。當然,如果給虛擬機器多啟用幾個核心的話,processing也許會佔上點便宜,畢竟目前thread模組是不能有效利用多cpu資源的(經實驗,stackless python在開啟雙核的情況下表現的效能和單核是一樣的,說明也是不能有效利用多cpu)。因此一種比較合理的做法是根據cpu的數量,啟用少量幾個程序,而在程序內部再開啟執行緒進行實際業務處理,這也是目前python社群推薦的有效利用多cpu資源的辦法。好在processing配合其自身提供的queue模組,程式設計體驗還是比較輕鬆的。

基於greenlet的實現則效能僅次於stackless python,大致比stackless python慢一倍,比其他方案快接近乙個數量級。其實greenlet不是一種真正的併發機制,而是在同一執行緒內,在不同函式的執行**塊之間切換,實施「你執行一會、我執行一會」,並且在進行切換時必須指定何時切換以及切換到哪。greenlet的介面是比較簡單易用的,但是使用greenlet時的思考方式與其他併發方案存在一定區別。執行緒/程序模型在大邏輯上通常從併發角度開始考慮,把能夠並行處理的並且值得並行處理的任務分離出來,在不同的執行緒/程序下執行,然後考慮分離過程可能造成哪些互斥、衝突問題,將互斥的資源加鎖保護來保證併發處理的正確性。greenlet則是要求從避免阻塞的角度來進行開發,當出現阻塞時,就顯式切換到另一段沒有被阻塞的**段執行,直到原先的阻塞狀況消失以後,再人工切換回原來的**段繼續處理。因此,greenlet本質是一種合理安排了的序列,實驗中greenlet方案能夠得到比較好的效能表現,主要也是因為通過合理的**執行流程切換,完全避免了死鎖和阻塞等情況(執行帶螢幕輸出的ring_greenlet.py我們會看到指令碼總是乙個乙個地處理訊息,把乙個訊息在環上從頭傳到尾之後,再開始處理下乙個訊息)。因為greenlet本質是序列,因此在沒有進行顯式切換時,**的其他部分是無法被執行到的,如果要避免**長時間占用運算資源造成程式假死,那麼還是要將greenlet與執行緒/程序機制結合使用(每個執行緒、程序下都可以建立多個greenlet,但是跨執行緒/程序時greenlet之間無法切換或通訊)。

stackless則比較特別,對很多資源從底層進行了併發改造,並且提供了channel等更適合「併發」的通訊機制實現,使得資源互斥衝突的可能性大大減小,併發效能自然得以提高。粗糙來講,greenlet是「阻塞了我就先乾點兒別的,但是程式設計師得明確告訴greenlet能先乾點兒啥以及什麼時候回來」;stackless則是「東西我已經改造好了,你只要用我的東西,併發衝突就不用操心,只管放心大膽地併發好了」。greenlet應該是學習了stackless的上下文切換機制,但是對底層資源沒有進行適合併發的改造。並且實際上greenlet也沒有必要改造底層資源的併發性,因為它本質是序列的單執行緒,不與其他併發模型混合使用的話是無法造成對資源的併發訪問的。

greenlet 封裝後的 eventlet 方案

eventlet 是基於 greenlet 實現的面向網路應用的併發處理框架,提供「執行緒」池、佇列等與其他 python 執行緒、程序模型非常相似的 api,並且提供了對 python 發行版自帶庫及其他模組的超輕量併發適應性調整方法,比直接使用 greenlet 要方便得多。並且這個解決方案源自著名虛擬實境遊戲「第二人生」,可以說是久經考驗的新興併發處理模型。其基本原理是調整 python 的 socket 呼叫,當發生阻塞時則切換到其他 greenlet 執行,這樣來保證資源的有效利用。需要注意的是:

在效能測試結果方面,eventlet 消耗的執行時間大致是 greenlet 方案的 3 到 5 倍,而 python 標準執行緒模型的 thread 方式消耗的執行時間大致是 eventlet 測試**的 8 到 10 倍。其中前者可能是因為我們在 eventlet 的測試**中,使用佇列機制來完成所有的訊息傳遞,而佇列上的訪問互斥保護可能額外消耗了一些運算資源。總體而言,eventlet 模型的併發效能雖然比 stackless python 和直接使用 greenlet 有一定差距,但仍然比標準執行緒模型有大約乙個數量級的優勢,這也就不奇怪近期很多強調併發效能的網路伺服器實現採取 eventlet 、執行緒、程序三者組合使用的實現方案。

分頁實現方法的效能比較

我們先給出幾種主要的分頁方法和核心語句,然後直接給出結論,有興趣的讀者可以看看後面的資料 幾種常用儲存過程分頁方法 topn方法 select top pagesize from tablename where id not in select top pageindex 1 pagesize id...

redis memcache 效能比較

from redis和memcache非常像的,都是key,value的方式,將資料存放記憶體中。最近在學習redis,在網上看了一些這方面的資料,有三種觀點 1,redis讀寫記憶體比memcache快 2,memcache讀寫記憶體比redis快 3,memcache讀寫記憶體比redis快,但...

apache resin ngnix 效能比較

靜態頁面測試,靜態頁面包括css,js,img ab c 20 n 30 併發測試 高數值的也測過,結論一致 resin2.17 平均響應時間為0.521ms nginx 0.7.65 平均響應時間為1.042ms 少許幾次小於1.042 apache2.0 平均響應時間為最低1.042ms 在re...