30 ポイント 投稿者 xguru 2023-05-24 | 9件のコメント | WhatsAppで共有
  • 非同期とマルチスレッド間のメモリ使用量を Rust、Go、Java、C#、Python、Node.js、Elixir で比較
  • 10秒間待機するタスクを N 個実行するプログラムを各言語で作成(ChatGPT の支援あり)
  • Xeon E3 + Ubuntu 22.04 で比較

結果

  • 最小フットプリント(タスク 1 個のみを実験): Go と Rust は 3MB 未満のみ必要、Python は 17MB、Java / Node.js は約 40MB、C# は 131MB
  • 1万タスク: Rust Tokio 4.6MB、Rust async-std 8MB、Go 28.6MB、Python 40MB、Rust Threads 48MB、Node.js 48MB、Java Virtual Thread 78MB、Elixir 99MB、C# 131MB、Java Threads 244MB
  • 10万タスク(スレッドは除外): Rust tokio 23MB、Rust Async-std 54MB、Node.js 112MB、C# 130MB、Java virtual threads 223 MB、Python 240MB、Go 269MB、Elixir 445MB
  • 100万タスク: Rust Tokio 213MB、C# 461MB、Node.js 494MB、Rust async-std 527MB、Java virtual thread 1154MB、Python 2232MB、Go 2658MB、Elixir 4009MB

結論

  • Rust tokio は群を抜いている
  • C# はフットプリントが大きいものの、非常に競争力がある(Rust を上回ることもある)
  • Go は 100万個になると Java の仮想スレッドとの差が広がる(Go が JVM より軽いという一般的な見方を覆す)
  • メモリ使用量だけを見たもので、ほかの要素は考慮されていない
  • 100万タスクでは処理開始時のオーバーヘッドが大きくなり、ほとんどのコードは完了までに 12 秒以上かかる
  • ほかのベンチマークも実行予定

9件のコメント

 
bus710 2023-05-25

Goを使いながらRustにも引き続き関心を向けていて、この窮屈な文法に適応する必要があるのか悩んでいる場合には、かなり意味のあるベンチマークですね。GoがOOMで落ちるような状況でもRustならしっかり耐えられるのなら……投資する価値は十分にありそうです。
もちろん、Rust開発者を確保するのがはるかに難しいのが、依然として問題ではありますが……

 
kuber 2023-05-24

Go は個々のゴルーチンごとにスタック(2KB)が1つずつ割り当てられるため、使用量が O(n) のぶん増えていく構造なので、スレッド数が増えるほど不利になるのは事実ですが……

ふと気になるのは、1万個を超えるスレッドを扱う状況がどれくらい頻繁にあるのかということです。実際にコードが動くことよりも、コンテキストスイッチのほうがより頻繁に発生しそうな気がします……

 
kotliner 2023-05-24

Kotlinコルーチンはどうなのか気になりますね

 
[このコメントは非表示になっています。]
 
secret3056 2023-05-24

Elixirの結果がいちばん驚きですね。ErlangはGoよりも軽く、数百ワード程度のメモリしか使わないと理解していたのですが……

 
kunggom 2023-05-24

Erlang公式ドキュメントを見ると、Erlangプロセスを1つspawnするのに338ワードが必要だそうです。そして64ビットシステムでは1ワードは8バイトなので、Erlangプロセス1つは約2.7KB(338 × 8 = 2,704)のメモリを占有することになります。Go言語ではgoroutineのスタック1つのサイズが約2.0KBとのことなので、メモリ消費はErlangのほうが大きいと見てよさそうです。

だとすると単純計算では、100万個のErlangプロセスは2.7GBのメモリを占有するはずですが、上で紹介されていたElixirベンチマークでは最大メモリ使用量が約4.0GB観測されていたので、さらに1.3GBのメモリが使われた計算になります。単純に計算すれば、このシナリオではErlangプロセス1つあたり1.3KBのメモリが追加で使われたことになりますが、詳しくは分からないものの、Erlangプロセス数がある一定の限度を超えて増えると、ランタイム側で何らかの追加メモリ領域の使用が必要になるのかもしれない、という気もします。

 
bus710 2023-05-25

Supervisorツリーマップやメッセージキューのキャパシティを確保していることが原因ではないかと推測しています。

 
humblebee 2023-05-24

Rustは、パラダイムからパフォーマンスまで、本当に素晴らしい言語だと思います。

 
xguru 2023-05-24

非同期方式とスレッド方式の比較に加え、言語ランタイムも絡んだベンチマークなので、見る観点によっては結論が変わり得るため、参考程度にご覧ください。
HNのコメントもあわせてご覧ください。 https://news.ycombinator.com/item?id=36024209