2 ポイント 投稿者 GN⁺ 2026-01-08 | 1件のコメント | WhatsAppで共有
  • 最近議論されたI/O性能とCPU処理速度の不均衡を実験で検証し、実際には依然としてCPUが主要な制約であることを示す
  • シーケンシャル読み取り速度はコールドキャッシュで1.6GB/s、ウォームキャッシュで12.8GB/sに達する一方、単一スレッドでの単語頻度計算は278MB/s程度にとどまる
  • コードの**分岐(branch)**構造がベクトル化(vectorization)を妨げ、単純な小文字変換の最適化でも330MB/s程度までしか向上しない
  • wc -wコマンドですら245MB/sにすぎず、ディスクよりもCPU演算と分岐処理がボトルネックであることを確認
  • AVX2ベースの手動ベクトル化で1.45GB/sまで引き上げたが、それでもシーケンシャル読み取り速度の約11%水準にとどまり、I/OではなくCPUがボトルネックであることを実証

I/O速度とCPU性能の比較

  • Ben Hoytの主張に従い、最近のシーケンシャル読み取り速度の向上がCPU速度の停滞を追い越したのかを実験
    • 同じ方法で測定すると、コールドキャッシュで1.6GB/s、ウォームキャッシュで12.8GB/sを記録
  • しかし単一スレッドで単語頻度計算を行うと、278MB/sにすぎない
    • これはキャッシュが温まった状態でもディスク読み取り速度の約1/5水準

Cベースの単語頻度計算実験

  • GCC 12でoptimized.c-O3 -march=nativeオプション付きでコンパイルし、425MBの入力ファイルで実行
    • 結果: 1.525秒を要し、278MB/sの処理速度
  • コード内の複数の分岐と早期終了がコンパイラのベクトル化最適化を妨げる
    • 小文字変換ロジックをループの外に移した後、330MB/sに向上
    • Clangを使うとベクトル化がよりうまく行われる

単純な単語数カウント(wc -w)との比較

  • 頻度計算の代わりに、単純に単語数だけを数えるwc -wコマンドを実行
    • 結果: 245.2MB/sで、予想より遅い
  • wc' ', '\n', '\t'などのさまざまな空白文字やロケール文字を処理する
    • 単純に空白だけを区切りとするコードより演算量が多い

AVX2ベースのベクトル化の試み

  • 最新CPUの機能を活用し、AVX2命令セットでベクトル化を実装
    • 256ビットレジスタを使用し、データを32ビット境界に整列
    • 空白文字との比較のためVPCMPEQB命令を使用
  • ビットマスク(PMOVMSKB)Find First Set(ffs) 命令で単語境界を検出
    • Cosmopolitan libcのstrlen実装から着想

性能結果と結論

  • 手動ベクトル化コード(wc-avx2)は1.45GB/sの処理速度を達成
    • wc -wと同じ結果(82,113,300語)であることを検証
  • コールドキャッシュ状態でも依然としてuserモードの演算時間が支配的
    • ディスクI/OよりCPU演算がボトルネックであることを確認
  • 全体としてディスク速度は十分に高速だが、分岐処理やハッシュ計算などのCPU演算が限界要因として残る
  • コードと実験結果はGitHub(haampie/wc-avx2)で公開されている

1件のコメント

 
GN⁺ 2026-01-08
Hacker Newsの意見
  • 現代のCPUの性能限界は、単一コアが処理できるデータ量、つまりmemcpy()速度によって決まると考えている
    ほとんどのx86コアは約6GB/s、Apple Mシリーズは約20GB/s程度だ
    広告で言う「200GB/s」のような数値は全コア合算の帯域幅にすぎず、単一コアは依然として6GB/s付近にとどまる
    したがって完璧なパーサーを書いてもこの限界は超えられない
    しかしzero-copyフォーマットを使えば、CPUが不要なデータを飛ばせるので、理論上は6GB/sを「超える」ことができる
    私が開発中のLite³フォーマットはこの原理を活用し、simdjsonより最大120倍高速な性能を示している

    • 提示された単一コアの数値は低すぎると思う
      たとえばZen 1は単一コアで25GB/sを示す(参考リンク
      私が書いたmicrobenchmarkの結果では、Zen 2はAVX未使用時で17GB/s、non-temporal AVX使用時で35GB/sまで出る
      Apple M3 Maxではnon-temporal NEONで125GB/sまで測定された
      したがってx86が6GB/s、Appleが20GB/sという数値は実態よりかなり低い
    • この限界がどこから来るのか気になる — コアとキャッシュ、あるいはメモリコントローラの間のバス構造のせいなのかと質問している
    • なぜApple Mシリーズがx86よりコアあたり3倍高い帯域幅を持つのか気になる
    • 最新のチップではCPUだけでメモリ帯域幅を飽和させるのは難しく、iGPUを活用する必要がある
      iGPUが統合メモリにアクセスできるからだ
      したがって大容量のメモリコピーや並列パース、圧縮/展開のような作業では、iGPUをblitterとして使うのが技術的に有利だ
      ただしzero-copyフォーマットでいう「スキップ」はキャッシュライン単位で行われる
    • SamsungのNVMe SSDは14GB/sの読み取り速度をうたっているが、CPU単一コアが6GB/sなら、この数字との関係が興味深い
  • 元記事の筆者はtimeコマンドの出力を誤って解釈しているようだ
    system時間は、カーネルがプロセスの代わりに使ったCPU時間だ
    例でreal 0.395s、user 0.196s、sys 0.117sなら、CPUは合計313msしか働いておらず、残りの82msはアイドル状態だった
    つまりディスクサブシステムより速く動いてはいたが、差はそれほど大きくない
    またI/OパスはCPUバウンドの状態でもある — ディスクとコードが無限に速くても、カーネルのI/Oコード実行に117ms必要だ

  • 記事の筆者です。続編があります: I/O is no longer the bottleneck, part 2

    • 以前、単語頻度カウント大会に参加したことがある
      参加者が使ったさまざまな最適化手法を扱った分析記事が面白い
      問題の複雑さや空白文字の分類数によってアプローチが変わっていた
    • もしこのテストが単一コアで実行されたのなら、上で主張された「6GB/sの限界」は実験的に反証されたことになる
  • 性能ボトルネックは常に「CPUかI/Oか」のような単一要因ではなく、実際のワークロードで最初に飽和する資源
    CPU、メモリ帯域幅、キャッシュ、ディスク、ネットワーク、ロック、レイテンシなど何でもありうる
    だから計測し、プロファイリングで証明し、変更後に再び計測しなければならない

  • 問題はCPUやI/Oではなく、**レイテンシ(latency)スループット(throughput)**のバランスだ
    ほとんどのソフトウェアはレイテンシを無視しているので遅い
    データをメモリ上に線形配置したり、バッチ処理や並列化を適用すればずっと速くなる

  • CPU ↔ キャッシュ ↔ 不揮発性ストレージだけで構成されたアーキテクチャを想像してみる
    mmap()malloc()と同じ性能特性を持つなら、プログラムメモリをファイル名で指定し、永続性をOSに任せることもできるはずだ
    いまなお多くのソフトウェア設計がハードディスク時代の制約に縛られている

    • ただしfsync()は依然として遅い
      真の永続性のためには、回転ディスクかどうかに関係なく別のアプローチが必要だ
    • Linuxでも似たことは実装できる
      実際、ほとんどのメモリ要求はmmap()を通じて行われる
      ただしカーネルがアクセスパターンを予測しにくいため、read/writeより遅くなることがある
  • クラウド環境では性能が料金プラン調整の手段になることもある
    ハードウェア性能は驚くほど進歩したが、一部のソフトウェア(特にWindowsやメッセンジャーアプリ)はむしろ遅く感じられる

    • 実際、クラウドインスタンスの性能はM1 MacBookの5分の1なのに、ずっと高い
      開発者向けのリモートワークステーションとしては非効率だ
    • たいていのGUIアプリが遅い理由は、いまだにI/O待ちのためだ
      TelegramやFB Messengerは速いが、TeamsやSkypeはそうではない
    • CRTモニターはデータをもっと速く表示していた
      一部のLCDには500msの遅延がある
  • NVMe SSDが初めて登場したとき、「これで2TB RAMを手に入れたようなものだ」と冗談を言っていた
    ところが最近のGPUサーバーは実際に2TB RAMを搭載している — 驚くべきエンジニアリングだ

    • 以前、中古のEpycサーバーで2TB DDR4 RAM構成が5000ドルだったのを見たことがある
      あのとき買っておけばよかったと悔やんでいる
  • 高い同時実行性の環境でOLAPデータベースを最適化した経験では、ボトルネックの大半はメモリ速度だった

  • I/Oボトルネックは本来シーケンシャル読み取りではなく**シーク(seek)**時間に関する概念だった
    記事の趣旨は理解できるが、この点は指摘しておきたい

    • CXL/PCIeのような最新技術のおかげで、いまやRAMやメモリコントローラも一種のI/Oデバイスと見なせる
    • 昔のデータベースの授業では、I/O性能はハードディスクのシーク時間で測っていた
      シーケンシャル読み取り速度はコードで改善できないので、非シーケンシャルアクセスの最適化が重要だった