2 ポイント 投稿者 GN⁺ 2024-11-30 | 1件のコメント | WhatsAppで共有

ベンチマーク

  • コルーチンとは?

    • コルーチンは、プログラムの実行を一時停止して再開できるコンピュータプログラムの構成要素であり、協調的マルチタスクのためのサブルーチンを一般化したもの。
    • 協調処理、例外、イベントループ、イテレータ、無限リスト、パイプなどのプログラム構成要素の実装に適している。
  • Rust

    • 2つのプログラムを作成: tokioasync_std を使用したプログラム。
    • どちらも Rust で一般的に使われる非同期ランタイム。
  • C#

    • C# は Rust と同様に async/await をサポートしている。
    • .NET 7 以降では NativeAOT コンパイルが提供され、VM なしでもマネージドコードを実行できる。
  • NodeJS

    • 非同期処理のために Promise.all を使用。
  • Python

    • asyncio モジュールを使用して非同期処理を実行。
  • Go

    • goroutine を使って並行性を実装し、WaitGroup を使って処理の完了を待機する。
  • Java

    • JDK 21 以降では仮想スレッドが提供されており、これは goroutine に似た概念。
    • GraalVM を使用してネイティブイメージを生成できる。

テスト環境

  • ハードウェア: 13世代 Intel(R) Core(TM) i7-13700K
  • OS: Debian GNU/Linux 12 (bookworm)
  • Rust: 1.82.0
  • .NET: 9.0.100
  • Go: 1.23.3
  • Java: openjdk 23.0.1
  • Java (GraalVM): java 23.0.1
  • NodeJS: v23.2.0
  • Python: 3.13.0

結果

  • 最小メモリ使用量

    • Rust、C# (NativeAOT)、Go はネイティブバイナリにコンパイルされるため、少ないメモリで動作する。
    • Java (GraalVM ネイティブイメージ) も良好な結果を示したが、他の静的コンパイル言語よりは多くのメモリを使用した。
  • 10K タスク

    • Rust はメモリ使用量がほとんど増加しない。
    • C# (NativeAOT) も少ないメモリを使用する。
    • Go は予想より多くのメモリを使用した。
  • 100K タスク

    • Rust と C# が良好な結果を示した。
    • C# (NativeAOT) は Rust より少ないメモリを使用した。
  • 100万タスク

    • C# がすべての言語を圧倒し、最も少ないメモリを使用した。
    • Rust もメモリ効率に優れている。
    • Go は他の言語に比べてメモリ使用量が多い。

結論

  • 多数の同時タスクは、複雑な処理を行わなくても相当なメモリを消費する可能性がある。
  • .NET と NativeAOT の改善は目覚ましく、GraalVM でビルドされた Java ネイティブイメージもメモリ効率に優れている。
  • goroutine は依然としてリソース消費の面で非効率的である。

付録

  • Rust (tokio) では join_all の代わりに for ループを使用することで、メモリ使用量を半分に削減した。今回のベンチマークでは Rust が絶対的な首位を占めた。

1件のコメント

 
GN⁺ 2024-11-30
Hacker Newsの意見
  • ベンチマークがNodeとGoの非同期処理方式の違いを適切に反映していない。NodeはPromise.allを使用し、Goはgoroutineを使用しているため差がある。非同期I/OとCPUバウンドな作業のメモリ使用量の違いを比較すると興味深いだろう

  • 「10秒間待機する作業」と「10秒後に起床する作業」の違いを説明している。Goコードのメモリ使用量が他のコードと比べて大きく異なる

  • GoとNodeを公平に比較するため、タイマーをスケジューリングするgoroutineと、タイマー信号を処理するgoroutineを使う方法を提案している。NodeにBunとDenoが含まれていないのは奇妙だと言及している

  • 多数の同時作業はメモリを多く消費しうるが、作業ごとのデータが数KB以上であれば、スケジューラのメモリオーバーヘッドは無視できる程度になる

  • 「同時作業」の定義によってメモリ使用量は変わりうる。効率的な実装では、100万の同時作業に約200MBが必要となる

  • Goがメモリ使用量でJavaに比べて2倍以上劣っている点を指摘し、このベンチマークは実際のプログラムを代表していないと言及している

  • 単純なコードで言語を比較するのは開発者にとって不公平になりうるため、実際の作業を追加してメモリ使用量とスケジューリングの違いを測定することを勧めている

  • ベンチマークはしばしば誤りだらけであり、このようなベンチマークを投稿する人たちの動機が理解できないと述べている

  • Javaのベンチマークが誤っている可能性があり、ArrayListの初期サイズを指定していないため不要なオブジェクトが大量に生成されている

  • Rustの非同期コードが予想より早く完了する理由を説明している。tokio::time::sleep()がfutureが生成された時点を追跡しているためだ