Go-Rust-Kotlin 的協程和隊列性能評測

綜述

現代的異步編程中有如下的幾個概念

由於協程是非常輕量的,所以可以在一個進程中大量的創建,runtime 會實際創建系統線程(一般爲恰好的物理 CPU 數),並將協程映射到實際的物理線程上執行,這個有時候稱爲 M:N模型。好的 runtime 會使得系統整體的性能隨着物理 CPU 的增加而線性增加。

Golang 是原生支持上述模型的語言,這也是 Golang 與衆不同的主要特性,在 Golang 中,通過關鍵詞 go 即可輕鬆開啓一個協程,通過關鍵詞 chan 則可以定義一個隊列,Golang 內置了調度運行時來支撐異步編程。

Rust 在 2019 年的 1.39 版本中,加入 async/.await 關鍵詞,爲異步編程提供了基礎支撐,之後,隨着 Rust 生態中的主要異步運行時框架之一 tokio 1 發佈,Rust 編寫異步系統也變得跟 Golang 一樣方便。

Kotlin 是一個基於 JVM 的語言,它語言層面原生支持協程,但由於 JVM 現在還不支持協程,所以它是在 JVM 之上提供了的調度運行時和隊列。順便,阿里巴巴的 Dragonwell JDK 在 OpenJDK 的基礎上可以選擇開啓 Wisp2 特性,來使得 JVM 中的 Thread 不再是系統線程,而是一個協程。JDK 19 開始增加了預覽版的輕量級線程(協程),也許在下一個 JDK LTS 會有正式版。

下表對比了使用這兩種語言對異步編程的特性支持

ekhdUa

根據場景的不同,選擇不同的隊列,不同的運行時,可以得到更好的性能,但 GolangKotlin 簡化了這些選擇,一般來說,簡化會帶來性能的損失,本文測評 Go/Rust(tokio)/Kotlin 的調度和隊列性能。

場景設計

測評的邏輯如下

  1. 創建 N 個接收協程,每個協程擁有一個隊列,在接收協程中,從隊列讀取 M 個消息

  2. 創建 N 個發送協程,於接收協程一一對應,向其所屬的隊列,發送 M 個消息

  3. 消息分爲三種類型

這個場景類似服務器的實現,當客戶端連接到服務器時,創建一個協程,接收客戶端的請求,然後將請求投遞給處理協程。

在這樣的邏輯下,有如下的幾個參數來控制測評的規模

NPGx2H

測評完成後,會輸出如下的幾個數據

nH6kTR

實現

源碼

以下是各語言實現時的一些額外說明

編譯

在安裝了 go、rust、JDK/maven 的機器上

git clone https://gitee.com/elsejj/bench-of-chain.gitcd bench-of-chainmake

運行

$ ./run.sh -w 5000 -e 10000 -q 256 -t 2program,etype,worker,event,time,speed
golang,str_ptr,5000,10000,0.477,104845454
rust,str_ptr,5000,10000,0.652,76636797
kotlin,str_ptr,5000,10000,1.638,30526077
$ ./run.sh -e 10000

6Pw6c0

結果

運行環境

V0QUIc

結果

./run.sh -e 10000

每個測評項會執行 5 次,取其平均值

結論和分析

從上述的運行結果來看

調度運行時和隊列

GC 的影響

其他

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/wb6qgKufTc1sXc1U2aPIRA