從JavaScript到Python之併發(上)
本文通過分析對比探究JavaScript與Python的併發能力,分上下兩篇,上篇探究CPU併發,下篇探究網路IO併發。
測試環境:
作業系統: win10 x64
CPU: Intel i5-8400 6核6執行緒
複製程式碼
用一個簡單的耗時功能來進行測試:遞迴實現斐波那契數列第n項的計算。
對於CPU密集型的計算任務,要通過併發提升程式執行效率,縮短執行時間,就一定要利用CPU的多核。 下面我們就來看看 JavaScript 和 Python 如何使用這3種方式進行併發以及對CPU核數的利用情況。
程式
程式是系統進行資源分配和排程的基本單位。
JavaScript
瀏覽器端執行的JavaScript
沒有多程式的概念,所以只考慮Node.js
上的多程式。
讓不同的程式執行在不同的核心上才能最好地發揮並行的優勢,為了達到這個目的,我們選取原生模組cluster
。
程式碼如下:
為了使結果更準確,開啟資源監視器進行檢視,CPU各個都已跑滿。
執行結果:
消耗時間(s): 8.107
消耗時間(s): 8.118
消耗時間(s): 8.16
消耗時間(s): 8.175
消耗時間(s): 8.209
消耗時間(s): 8.253
複製程式碼
所以總耗時在8.253秒。
為了進一步證實,執行單程式程式碼:
單程式執行結果:
消耗時間(s): 7.361
時間比多程式略少一些,考慮到程式的建立與銷燬所耗的時間,在誤差範圍之內。
Python
Python
操作程式的模組為multiprocessing
,同樣我們啟動6個程式看看能不能把所有核跑滿。
程式碼如下:
CPU監控也顯示6個核都執行了計算任務:
執行結果
消耗時間(s): 8.683971881866455
消耗時間(s): 8.699971675872803
消耗時間(s): 8.71097183227539
消耗時間(s): 8.84197187423706
消耗時間(s): 8.863972425460815
消耗時間(s): 8.900972843170166
複製程式碼
總執行時間約為8.9秒。
執行時間看上去只比JavaScript
慢了那麼一丟丟,但是需要注意的是JavaScript
程式碼計算的是數列第45位,Python
只計算第38位!
同樣執行一下單程式進行計算,與多程式進行對比驗證。
消耗時間(s): 8.092010259628296
執行時間也相當接近。
結論
JavaScript(Node.js)
和Python
都提供了操作程式的原生模組,多程式執行計算任務時都能有效利用CPU多核提升效率。
執行緒
一個程式可以有一個或多個執行緒,執行緒相對程式而言更輕量,上下文切換成本更低,通常作為併發操作的首選。
JavaScript
前端工程師很少了解執行緒的概念:瀏覽器端直到HTML5標準提出才支援以web worker
方式建立多執行緒,Node.js也是12以後的版本才支援使用worker_threads
模組進行多執行緒計算。
總結起來就是早期的JavaScript
執行環境沒有執行緒相關模組與API,對開發者遮蔽了執行緒的概念。
web worker
瀏覽器端多執行緒執行執行JavaScript
程式碼分為兩部分,待執行的js
檔案和引入該js
檔案的頁面。
程式碼如下:
在本地啟動和訪問伺服器,得到監控影象也是多核執行:
執行結果:
消耗時間(s): 15.989
消耗時間(s): 16.109
消耗時間(s): 16.204
消耗時間(s): 16.874
消耗時間(s): 16.876
消耗時間(s): 16.971
複製程式碼
總執行時間約為16.9秒。
為了使結果更有說服力,我們在瀏覽器控制檯執行一下相關程式碼
時間略少於多執行緒執行。
相對於Node.js端執行多了1倍,看來瀏覽器端的執行緒效能並不高。
worker_threads
不能利用多核一直讓Node.js
飽受詬病,新版本算是對這個問題打了個補丁。
Node.js
端程式碼如下:
CPU監控結果:
執行結果:
消耗時間(s): 8.229
消耗時間(s): 8.282
消耗時間(s): 8.299
消耗時間(s): 8.403
消耗時間(s): 8.417
消耗時間(s): 8.44
複製程式碼
總時間8.44秒與單獨執行相當。
結論
瀏覽器和Node.js
都支援多執行緒,但明顯瀏覽器執行效能不如Node.js
,所以使用web worker
的工程師需謹慎。
Python
Python
的原生模組threading
提供多執行緒操作。具體程式碼如下
看波形圖應該是利用了各個核,但並沒有滿負荷執行。
執行結果也不盡如人意:
消耗時間(s): 38.476489543914795
消耗時間(s): 41.6956250667572
消耗時間(s): 45.613648414611816
消耗時間(s): 47.564653396606445
消耗時間(s): 47.68662452697754
消耗時間(s): 48.57262468338013
複製程式碼
總時間為48.5s,是單程式執行時間的6倍。
結論
從輸出結果時間來看,Python
的執行緒是併發執行了,但並沒有提升執行效率。
協程
協程和執行緒有些類似,區別在於執行緒是由作業系統排程的,協程是由使用者程式碼管理的。 相對執行緒而言上下文切換成本更輕量。
Python
抱歉,JavaScript
的原生模組目前還不支援。
Python3
倒是積極引入了協程的概念,實際作用看測試結果吧~
程式碼:
CPU監控:
執行結果:
消耗時間(s): 8.070001602172852
消耗時間(s): 16.14399790763855
消耗時間(s): 24.214999198913574
消耗時間(s): 32.300398111343384
消耗時間(s): 40.38105368614197
消耗時間(s): 48.451064109802246
複製程式碼
結論
協程在CPU使用率上和執行緒差不多,並沒有提升。而且從輸出結果來看並不是真正的並行執行。
總結
- 首先不考慮併發的情況下,
JavaScript
的執行效率要優於Python
。 - 兩者都能利用多程式有效地利用CPU多核提升效率。
- 執行緒
JavaScript
更勝一籌,Python
在受限於全域性直譯器鎖的情況下,多執行緒可以實現併發但不能提升效率。 - 協程
JavaScript
完敗,但Python
的協程也不擅長處理CPU密集型操作。
原文連結:tech.gtxlab.com/js2py-async… 作者資訊:朱德龍,人和未來高階前端工程師。