
2026/06/13 13:37
CPU フィジクスと CPU クロック周期について
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
改善された要約:
本書の第 4 章用の草案であり、概ね 2026 年時点に適用可能な代表例のマザーボードアーキテクチャを用いて、CPU 内部と外部ストレージとの間の巨大なレイテンシギャップを説明しています。単純なレジスタ間(R-R)演算では約 1 サイクルしかかからない(乗算は 3〜6 サイクルかかるが)、メインメモリのアクセスには信号伝搬の限界(寄生キャパシタンス)および距離のため、200〜300 サイクルという深刻なペナルティが課されます。メイン RAM は L1 リードに比べて約 100 倍遅いのです。その結果、現代の CPU はスループットを維持するために深層パイプライン化、超多倍実行(1 サイクルあたり 10〜12 命令可能)、動的分岐予測に依存していますが、キャッシュミスや TLB ミスは大きな低速化を引き起こします。
最適化戦略は、単に計算ロジックに頼るのではなく、データを高速キャッシュ(L1/L2)に収めることを優先すべきです。具体的なコーディング慣行は極めて重要であり:スタック変数はほぼ確実にキャッシュされ、一方で静的/グローバル変数は
.bss/.data セグメントにマッピングされます。また、ヒープデータは直線アクセスでない限り高いレイテンシを招きます。さらに、開発者は [[likely]]/[[unlikely]] 属性が標準的なフローではほとんど効果が薄い一方、エラー処理においては不可欠であることを理解する必要があります。また、巨大ページを使用することで TLB ミスのコストを軽減できることも知っておくべきです。これらのアーキテクチャ上の制約—including I/O 操作の巨大なレイテンシ(例:fsync、ネットワーク往復時間)と CPU スピードとの相対的な関係—を無視することは、概ね 2026 年およびそれ以降のシステムにおいて著しい性能劣化をもたらします。本文
現代用 64 ビット CPU に向けた効率的な C++ プログラミング:CPU フізикаとクロック周期(初編)
第 1 巻の forthcoming(出版予定)書籍『現代用 64 ビット CPU に向けた効率的な C++ プログラミング』 (Efficient C++ Programming for Modern 64-bit CPUs) の第 4 章「CPU フİZикаと CPU クロック周期について」の初編草案です。
「効率化への尽力がもたらすもう一つの利点は、それがあなたに問題をより深く理解させることとなる。」 —— 元 STL の作者 Alex Stepanov
本稿では、事実上の矛盾点などを見出された場合、ご指摘を歓迎いたします。修正いたしますので、お気軽にコメントください。
前提:電気信号と物理距離の制約
画像の詳細に入る前に重要な仮定を示します。
- 法則: 電気信号が物理的に移動する距離が遠いほど、アクセス速度が遅くなる。
- 原因: 電子信号の特徴的な時間は、接続長の比例する寄生容量(parastic capacitances)によって制限される。
- 普遍性: これは優れた設計の電子機器におけるほぼ普遍的な法則である。
- 距離と光速の関係:
- 光速度による限界(0.5 mm 往復約 (3 \times 10^{-12}) 秒)。
- 現実の R-R(レジスタ間)操作(概ね (3 \times 10^{-10}) 秒、光速制約の約 100 倍)。
- 結論: CPU コアにおいて速度が制限されるのは、信号伝搬そのものではなく、寄生容量による遅延である。
Core(コア)レベルの詳細
基本動作とパイプライン化
などのコードは、通常レジスタからレジスタへの(R-R)操作だが、以下のように動作する:a += b;- データがレジスタから ALU(または SIMD)へ送られる。
- ALU で演算され、結果が戻ってくる。
- 現代 CPU の実態:
- すべての 64 ビット CPU はパイプライン化されている。
- ALU がデータを必要とする頃には、データは既に入っていることが多い。
- そのため、レジスタ間往復の遅延ペナルティは回避可能。
演算コストとスーパースカラ技術
- 単体演算のコスト:
- 加算・減算/ビット演算:1 クロック周期
- 乗算:概ね 3〜6 クロック周期
- 除算:最大 20 クロック周期(過去は 100 クロック周期以上だった)。
- スーパースカラ(Super-scalar)構造:
- CPU コア内には複数の ALU および SIMD ユニットが存在する。
- 同じクロック周期内で複数の操作を処理できるため、結果として「0.75 サイクル」のように見える微ベンチマークが生まれることもある(これは実際の半分以下の時間で完了するためではなく、4 個のうち 3 個が空で終わるなどの統計的効果による)。
- RIPC(Retired Instructions Per Cycle):最大 10〜12 に達する可能性がある。
- 注記: 実用上 RIPC=4 を達成するのは大変な挑戦であり、通常は ALU bound アルゴリズムに限定される(RAM bound とは対照的)。
メモリアクセスとキャッシュ
- L1 キャッシュの構成:
- L1D(データ)と L1I(指令)に分割されている。
- アクセスコスト:
- L1D 読み込み: 約 3 クロック周期。
- L1D 書き込み: CPU の視点では「ほぼ瞬時」(命令送出のみで待機不要)。
- L2 キャッシュ ミス: 10〜15 クロック周期(L1 に比べてはるかにコスト高)。
ブランチミスペリクションと [[likely]]/[[unlikely]]
[[likely]]/[[unlikely]]ブランチ予測の仕組み
- コンパイラとハードウェアは、データを到着させてから指令を実行する(データ関連遅延は主流)。
- 例外:ブランチ(分岐)指令(例: JZ, JNC, BEQ)。
- 条件計算に必要なデータの取得に時間がかかるため、ストールが発生する可能性がある。
- CPU は推測予測を行い、正解と仮定して先に進める。
- ミスした場合: 予測に基づいた計算を破棄し、ブランチ指令からやり直す。
コストと最適化戦略
- ミスパイナルティ(ブランチミスペリクションのコスト): 概ね 15〜25 クロック周期 [Fog04, セクション 7.12]。
- 開発者の対策:
- ブランチが正しく選択される確率を高めることでペナルティを回避する(例: 99% の場合)。
- 50:50 のランダムなブランチについては、どんなに頑張っても半分はミスるため効果なし。
ヒューリスティクスと C++ アトリビュート
- ハードウェアヒューリスティクス:
- 例:
が後方(ループ先頭)への分岐はループとして認識され、正しいと予測される。JNZ
- 例:
- C++
の役割:[[likely]]/[[unlikely]]- 動的ブランチ予測器に**「過去の統計」ではなく「初期の傾向」**を教えるために使用される。
- 比較的低頻度で利用されるため、汎用的な効果は限定的。
- 推奨使用方法:
- 「非常に非可能性」以外の確率は推測が難しいため、無条件での使用は避けるべき。
- 正当な使用ケース:
- エラーハンドリング(エラーは稀)。
- 明確な境界ケース(例:
の巨額な値、sin(x)
の big-int パース)。strtod()
TLB(Translation Lookaside Buffer)について
- 機能: MMU を持つ CPU で、仮想アドレスを物理アドレスへ翻訳するキャッシュ。
- 通常 2 レベルあり、実質的に L1 キャッシュの上位に位置する。
- DTLB misses(データ用 TLB ミス):
- ベクター指向プログラムでは、ノードベース構造よりも影響が小さい。
- 線形データ構造(ベクターなど):TLB 圧力が最小化される。
- 古典的 C スタイル(リンクリストや木など):より影響を受けやすい。
- 実際にはアプリケーションレベルで問題になることは稀 [Drepper, セクション 6.2.4]。
- ITLB misses(指令用 TLB ミス):
- 巨大なプログラム(Clang など)に限られることが多い。
- 巨大ページングによる対処が可能 [Bakhvalov, セクション 11.8]。
キャッシュ階層全体での遅延
- L3 キャッシュ:
- コア同士で共有される。
- 読み込みコスト:概ね 30〜70 クロック周期。
- メイン RAM:
- L1 の約 100 倍の遅延(200〜300 クロック周期)。
- x64 デスクトップと Apple SoC (M4) ともにこの範囲にある。
- チップ外へのアクセス:
- DDR インターフェースを通り、チップ外へ出ると速度が急激に低下する(寄生容量の増大)。
メモリストレージと C/C++ の観点
変数の格納場所とパフォーマンス特性
| ストレージの種類 | C++ 用語 (Duration) | 特徴・コスト |
|---|---|---|
| スタック | automatic storage duration | - CPU レジスタへ適合しない場合 - ハードウェアサポート(SP ポインタ) - ほぼ確実にキャッシュされる - スレッド特有、サイズ制限あり (通常 MB 級) |
| 静的変数 | static storage duration | - グローバル・関数内静的変数など - アドレスはコンパイル時に既知( , セグメント)- コンストラクタなしで初期化可能 - キャッシュされやすいが、スタックほど確実ではない |
| ヒープデータ | dynamic storage duration | - , , など- 巨大になりやすく、ページングを発生させる可能性あり - 「未キャッシュ」とみなすべき(隣接領域のアクセスを除く) - 線形スキャンにはメリットがあるが、ランダムアクセスには不利 |
| スレッドローカル変数 | thread storage duration | - キーワード- 各スレッド固有のメモリ空間 - 実質的に「スタックのようなポインタ管理」 |
IOHO(I/O Hierarchy of Objects):上記は C++ メモリの簡潔なモデルです。詳細については [Ketron] や [Roy] の資料を参照してください。
外部世界との接続と遅延
ネットワークアクセスの概算
| 接続形態 | 推定クロック周期 | 時間換算 |
|---|---|---|
| 1Gbit 有線 LAN | 100,000〜500,000 | ~30〜150 µs |
| ローカル Wi-Fi | 3,000,000〜30,000,000 | 1〜10 ms |
| ISP 到達(光ファイバー) | 15,000,000〜80,000,000 | 追加で 5〜25 ms |
| ケーブル/DSL | 60,000,000〜150,000,000 | 20〜50 ms |
| 4G LTE / 5G | 100,000,000〜200,000,000 | 30〜70 ms |
インターネット上のサーバーへの往復遅延(例)
- 物理距離による影響: 光速の制限により、都市間の距離が直接関係します。
- トロント ⇔ モントリオール (~500 km): 約 8 ms
- ダブリン (~5,200 km): 約 85 ms
- パー思 (~18,000 km): 約 250 ms
具体的なシナリオ:トロントからモントリオールへアクセス
- 経路: Wi-Fi (ローカル) → DSL (ISP) → インターネット (物理伝搬)。
- 合計遅延の概算:
- Wi-Fi: 3M〜30M クロック周期
- DSL: 60M〜150M クロック周期
- 物理伝搬 (+ サーバー側): 約 24M クロック周期
- 結果: 100,000,000〜200,000,000 クロック周期(約 30〜60 ms)。
- 最悪ケース: ネットワーク障害時は「実用的な目的において無限大」となり、ユーザーは放棄してしまうことがあります。
エンタープライズレベルのストレージパフォーマンス (ACID 保証時)
データベースなどで絶対的に必要な
の遅延(フル ACID 書き込み)です。fsync()
| ストレージタイプ | クロック周期 | 時間換算 |
|---|---|---|
| **エンタープライズ RAID **(バッテリーバックアップ付き) | 約 120,000 | 40 µs |
| エンタープライズ NVMe SSD | 450,000 | 150 µs |
| コンシューマー SATA SSD | 1,500,000〜30,000,000 | 0.5〜10 ms |
| **HDD **(スピンドルアップ不要、シーク時間込み) | 50,000,000〜200,000,000 | 17〜66 ms |
リファレンス
- [Fog] Agner Fog: 命令表 (Intel, AMD, VIA CPU の命令遅延、スループット、マイクロオペレーション分解)。
- [Fog04] Agner Fog: C++ でソフトウェアを最適化する, セクション 7.12。
- [Drepper] Ulrich Drepper: メモリのパート 5:プログラマーができること, セクション 6.2.4。
- [Bakhvalov] Denis Bakhvalov: 現代 CPU におけるパフォーマンス解析とチューニング, セクション 8.4, 11.8。
- [JiaEtAl] Weiwei Jia ら:ネスト化仮想化における TLB Miss 削減のための効果的な巨大ページ戦略。
- [Trudeau] Yves Trudeau: ストレージデバイス上の Fsync パフォーマンス。
- [Ketron] Dominick Ketron: モダン C++ メモリ最適化。
- [Roy] Patrice Roy: C++ メモリ管理。
- [Josuttis] Nicolai Josuttis: 共有ポインタのコスト、あるいは参照傳遞によるパッシングが有用である理由。
- [Cordes] Peter Cordes: L2 TLB miss の後に何が起きるか への回答。