# プロセッサを支える技術

## 概要

### 本

* [プロセッサを支える技術　－－果てしなくスピードを追求する世界 (WEB+DB PRESS plus) | Hisa Ando](https://amzn.to/2VHhcgf)

### かかった時間

* 28.6 時間

### 読む前の状態

* [CPUの創りかた | 渡波 郁](https://amzn.to/2psdOcR) を読んで、CPU の基本的な仕組みを学んだ
* [コンピュータシステムの理論と実装](https://y-meguro.gitbook.io/reading-record/computer-systems/nand2tetris) で、論理ゲートから高水準言語までのなんとなくの全体観はあった

### 読む前後の変化

* プロセッサの変遷と、利用されている技術の概要を掴むことができた
* 現代の CPU の偉大さを感じることができた

### まだわからないこと

* どのような技術が使われているか知識は入ったが、触っていないので理解度は低め
* 知識自体もそこまで深くない気がするので、もう少し深ぼった方がいいかも

## 詳細メモ

### 1章: プロセッサとコンピュータシステムの基礎

* コンピュータの基本構造、データの表現、命令の説明
* 半導体技術の進歩の概観（ムーアの法則とデナードスケーリング）
* プロセッサの性能向上の 3 本柱は「クロック周波数の向上」「並列処理」「機能拡張」
* コンパイラを使うプログラミングとインタプリタによるプログラミングの違い

### 2章: プロセッサの変遷

* コンピュータコンピュータ以前の計算装置（そろばんや機械式）と初期の電子式コンピュータ。関係ないけど Computer History Museum いつかまた行きたいと思った
* プロセッサを構成する素子の変遷（真空管 → トランジスタ → 集積回路 → 大規模集積回路）とそれぞれの特徴
* 1971 年の Intel4004 と 2010 年のマイクロプロセッサとの比較
  * トランジスタの数: 2300 → 数億〜数十億
  * 配線幅: 10μm → 50nm（1/200）
  * クロック周波数: 740kHz → 3GHz（4000 倍）
* VLSI のプロセッサ数は 20 年で 10000 倍（1.5 年の期間で倍になるペース）の増加傾向、クロック周波数は 10 年で 10〜20 倍の傾向。ただしクロック周波数は消費電力の制約から、向上のペースは鈍化している
* 命令アーキテクチャの発展
  * プログラム内蔵式コンピュータ / 仮想メモリ / マルチプロセス / メモリ管理機構 / 特権状態 / ISA 拡張
* マイクロアーキテクチャの発展
  * パイプライン処理 / 演算器の高速化 / RISC と CISC / スーパースカラ実行 / Out-of-Order 実行 / 分岐予測 / キャッシュ / マルチコア

### 3章: \[詳説] プログラマのためのプロセッサアーキテクチャ

#### マイクロアーキテクチャを支える技術

* パイプライン実行の仕組み
  * パイプラインで問題となるのは「構造的ハザード」「データハザード」「制御ハザード」
  * 構造的ハザード: "プロセッサの資源を取り合う"問題
    * 「命令に優先度をつける」「資源を追加する」ことで対応
  * データハザード: "前の仕事が終わらないと次に進めない"問題
    * 1 サイクルで演算できるものはレジスタバイパスで対応
    * 複雑な演算はソフトウェアパイプライン処理で対応
  * 制御ハザード: "条件分岐命令"問題
    * 関数のインライン展開（コール / リターンを減らす）、ループアンローリング（条件分岐を減らす）で対応
* キャッシュの仕組み
  * タグとキャッシュラインで管理、メモリとキャッシュ間のデータ転送はキャッシュライン単位でまとめて行う
  * キャッシュの方式
    * フルアソシアティブ方式（インデックスなし）/ ダイレクトマップ方式（ラインアドレスごとに 1 つのキャッシュライン）/ セットアソシアティブ方式（複数のキャッシュライン）
    * 参考: [【図解あり】キャッシュメモリを分かりやすく説明する | Yone's Archive](https://elite-lane.com/cache-memory/)
* RISC と CISC
  * CISC 命令だと命令のデコードが難しく、またパイプライン実行が難しい
  * x86 では CISC 命令を RISC 風の内部命令に変換してから実行（互換性を維持）
  * RISC の特徴はロード命令とユース命令を引き離すことで無駄な待ち時間を減らせることと、命令の格納に多くのメモリを必要とすること（2〜3 割程度）
* 演算器の高速化
  * 整数加算器の高速化: プレフィックスアダーを利用（32 ビットの加算の場合、全体で 18 段程度の論理ゲートとなり、25 段以内なので 3GHz クロックなら 1 サイクルで実行できる）
  * 整数乗算器の高速化: ブースエンコードとウォレスツリーを組み合わせると、64 ビットの乗算の場合、合計 3 ステージ程度
  * 除算の高速化: SRT 除算アルゴリズムで速くなるが、それでも時間がかかる。できるだけ除算を使わないようにしたり、除算同士が近接しなように気をつけたりすることが大切
* スーパースカラ実行の仕組み（構造的ハザードを解消する）
  * 1 サイクルに複数の命令を並列に実行するプロセッサをスーパースカラプロセッサ、その動作をスーパースカラ実行と呼ぶ
  * 最近のハイエンドプロセッサでは、4〜6 命令を並列に実行する
  * スーパースカラプロセッサでは、「連続する複数の命令が並列に実行できるか」がポイントとなる
* Out-of-Order の仕組み（データハザードの影響を軽減する）
  * 機械命令の順番を崩して、後の命令でも実行可能な命令を先にやってしまうのが Out-of-Order 実行
  * Out-of-Order 実行を行うためにリザベーションステーションという機構が用いられる
  * ただし、逆依存性の問題がある。逆依存性がある場合、命令の順序を入れ替えて実行してしまうと、結果が変わってしまう
  * 逆依存性にはリネーム処理で対応する。また例外発生時は直前の状態に戻す
  * またメモリバリア命令を設けて、実行順序を保証する
* 分岐予測の仕組み（制御ハザードによる損失を低減する）
  * 分岐予測は、プロセッサが条件分岐命令の分岐方向を予測し、予測した方向の命令を読み出し、解釈、実行していく技術
  * 飽和カウンタを使う方法 / 履歴を使う方法（ローカル履歴、グローバル履歴）などがある
* メモリ、I/O と入出力インタフェース
  * 2009 年に発売された Nehalem アーキテクチャ以前の Intel プロセッサでは、コモンバスという方式が使われていた
  * コモンバスはコマンドバス・アドレスバス・データバスの 3 つのバスを持つ
  * 欠点は以下
    * 一時には 1 つの装置しかバスを使えない
    * 多くの装置を接続するので電気的にも高速の動作が難しく、最高でも 1GHz 程度のデータ伝送に制限される
    * バンド幅を確保するためには多数のピンを必要とする
  * これらの欠点に対応するため、最近のプロセッサでは「メモリコントローラをプロセッサチップに内蔵し、メモリ専用のインタフェースを設けて DIMM を直結する」形になっている
  * 入出力装置を制御する I/O 制御レジスタをメモリ空間に置く方式はメモリマップド I/O と呼ばれる
    * Intel の x86 プロセッサでは、メモリ空間と別に I/O 空間というアドレス空間がある
* パフォーマンスカウンタ
  * プロファイラによって、関数ごとに全体の何％の場合で実行中であったかという情報を出力できる
  * パフォーマンスカウンタによって、プロセッサが実行した命令数・各レベルのキャッシュのアクセス回数・各キャッシュのミス回数・実行された条件分岐命令・予測ミスとなった条件分岐命令などの値をカウントできる

#### プロセッサの利用範囲を広げるアーキテクチャ拡張

* マルチプログラミングとメモリ管理機構
  * メモリ上に細かい空き領域が散らばって多数残ってしまい、空き領域の合計は大きいが、新しいプログラムを入れられないような状態をフラグメンテーションという
  * この問題の対応として、ページ方式のメモリ管理が行われる
  * 論理ページとそれに対応する物理アドレスや属性の情報を、ページテーブルに記録する
  * ページテーブル用のキャッシュを TLB という
  * ページテーブルを現実的なサイズに抑えるため、多階層のアドレス変換を行う
* 割り込み処理機構
  * プロセッサの正常な命令実行の流れを中断する必要がある事象を「例外」。以上の原因となっている命令が特定できる例外を「トラップ」、特定できない例外を「割り込み」と呼ぶ
  * ポーリング: OS がステータスレジスタの状態を読んで動作状態を知り、完了していない場合はある程度の時間をおいて読むことで、I/O 動作の完了を知る方法
  * 割り込み: 動作が完了したら I/O コントローラのほうからプロセッサに完了連絡をする。割り込みメカニズムは、I/O 動作の完了以外にもエラーが行った場合の異常終了の通知などにも使われる。
  * 割り込みには様々なものがあるので、緊急度を考慮して高速に割り込み処理ができるように改良したものを「ベクタ割り込み」という。また、割り込み緊急度を示す情報を「割り込みレベル」という
* 仮想化のサポート
  * OS レベルでの分離を行うために、VMM というレイヤを設けて、1 つのプロセッサハードウェアを複数のハードウェアがあるように見せる「仮想化」という方法が考えられた
* マルチメディア、暗号などのサポート
  * 1 つの命令で複数の演算器に同じ動作をさせる並列処理のやり方を「SIMD」という
  * DES に代わる新しい標準暗号となる共通鍵暗号アルゴリズムとして制定されたのが「AES」という暗号方式

#### x86 Nehalem アーキテクチャのプロセッサ

* Core i7 プロセッサの構成
  * 2010 年時点で、Intel プロセッサの最新のアーキテクチャは Nehalem アーキテクチャと呼ばれる
  * 命令フェッチとプリデコードからデコードまでの部分が、命令を供給する「フロントエンド」と呼ばれる部分
  * 命令実行を行う「実行エンジン」ではリネームからはじまり、リオーダバッファ・リザベーションステーション・レジスタを介して、実行パイプラインに送られる
  * 最後の「キャッシュ階層」では 1 次命令キャッシュ・1 次データキャッシュ・2 次キャッシュ・3 次キャッシュを持つ
* メモリ管理は 4 階層のテーブルを使用
  * 4 階層アドレス変換機構で、ページテーブルを管理。これでページテーブルに必要なメモリ量を大幅に削減
* 新しいプロセッサインタフェース QPI（Quick Path Interconnect）
  * 複数の CPU チップ間を接続してマルチソケットシステムを構成する場合のインタフェースとして QPI と呼ぶ高速伝送インタコネクトを使う。これはコモンバスではなく、1 対 1 の接続とすることで、高速伝送を可能にしている

### 4章: 仮想化サポート

#### 仮想化の目的、メリット、デメリット

* 「仮想化」は 1 つのハードウェアを多数に見せる技術。VMM あるいはハイパーバイザと呼ばれる、OS とハードウェアの間に入るソフトウェアによって実現される
* 仮想化のメリット
  * 強固なユーザ間分離を実現する（OS から VMM は見えないので、侵入が困難）
  * 複数サーバーをまとめて稼働率を改善できる（サーバーをまとめてもピーク負荷はほとんど増加しない）
* VMM の実行オーバーヘッド（仮想化のデメリット、注意点）
  * プロセッサは比較的短い時間で切り替えられるが、メモリ切り替えに時間がかかる。すべての OS の上で動く共用のメモリを与えると、必要なメモリは大量になる
  * また、仮想マシン間で割当時間に不平等が発生しないように、各仮想マシンに対してプロセッサ時間、メモリ量、ディスクの使用量などの資源の割り当てを管理する必要がある

#### 仮想化を実現するために

* OS に独立の（仮想）ハードウェアを提供する VMM
  * VMM の上で動く OS だということをはっきりさせる場合、「ゲスト OS」と呼ぶ
  * VMM は各ゲスト OS に対して、別々のメモリ空間を提供する必要がある。そして、ゲスト OS が物理アドレスと思っているものは各ゲスト OS 個別の「仮想物理アドレス」で、VMM が本当の物理アドレスに変換する
  * VMM はゲスト OS のハードウェアアクセスをインターセプトする

#### 仮想化をサポートするハードウェア機構

* ハードウェア操作命令の検出
  * 「特権状態」「ユーザ状態」の区別を利用する。仮想化を行う場合は VMM が特権状態、ゲスト OS はユーザ状態で実行されるが、ゲスト OS が特権命令を実行しようとするのを検知して、特権違反例外としてゲスト OS のハードウェア資源操作を検出、横取りする
* ハードウェア状態の退避、復元
  * 仮想化を行う場合、実プロセッサは時分割で複数の仮想マシンを演じることになる
  * また、特権状態で操作できる各種のレジスタを退避、復元する必要がある
* 二重のアドレス変換、TLB（Tlanslation Lookaside Buffer）
  * 論理アドレスと二重にアドレス変換を行った結果の対応を TLB に保存しておく
  * ハードウェアが自動的にページテーブルを読んで変換結果を TLB に書き込んでくれるハードウェアテーブルウォーク機構を持っている場合は、ページテーブルが切り替わるたびにシャドウテーブルを作り直す必要がある
    * これに対して、ゲスト OS に VMM の介在なしに自分のページテーブルを変更する権限を与え、シャドウページを作る手間を減らすのが仮想 TLB 方式
* I/O の仮想化
  * 3 種類の方式がある
    * 正統的な I/O 仮想化: VMM が I/O をエミュレートする
    * パススルー方式: 入出力装置を 1 つの仮想マシンに割り当てる
    * Para Virtualization（準仮想化）方式: ハイレベル I/O 要求を使う
  * エミュレーション方式では性能が落ちるが、他の 2 つだと仮想化を行わない場合とほとんど変わらない
* ライブマイグレーション
  * VMM は仮想マシンの状態を制御情報として退避できるので、この情報を他のプロセッサに復元すれば、移動先のプロセッサで元のプロセッサと同じ仮想マシンの状態を再現できる
  * 1 台のサーバで動作中のアプリケーションを他のサーバに移動して動作を継続させることを「ライブマイグレーション」という

### 5章: マルチプロセッサの出現と普及

#### マルチスレッドプロセッサ

* 用語のおさらい
  * 複数のスレッドを並列に実行できるハードウェア機能を持つプロセッサを「マルチスレッドプロセッサ」と呼ぶ
* マルチスレッドの 2 つの方式
  * VMT（垂直マルチスレッド）: 複数のスレッドを切り替えて実行する
  * SMT（同時マルチスレッド）: 複数のスレッドの命令を混ぜて実行する
* VMT の仕組み
  * スレッドの状態を記憶するレジスタや、実行する命令を指すプログラムカウンタなどは、スレッド個別に持つ。演算器やキャッシュなどはスレッドごとに持たず、共用する
  * 1 つのスレッドを追加するための面積増加は数％程度
* SMT の仕組み
  * 一方のスレッドのバブルサイクルに他のスレッドの命令を詰め込む
  * VMT と同様にスレッド数分のプログラムカウンタとレジスタファイルのセットを持つ
* マルチスレッドの効果
  * マルチコア / マルチスレッドどちらの場合でも、OS は独立の CPU として認識する
  * スレッド間で資源競合してしまうと、性能が低下する場合もある（キャッシュミスの増加など）
  * 実行するスレッドの性質によるが、一般的に、SMT のほうが VMT より性能改善は大きくなる（また追加ハードウェア量も増える）
  * 平均的には 2 スレッド化した場合、スループット性能は 10〜30％程度向上すると言われる
  * マルチスレッドは通常のプロセッサコアに、比較的小さいハードウェアの追加で 2〜4 スレッドを実行できるようにする機構
    * 主な目的は実行機構の遊びサイクルを減らして有効利用すること
  * マルチコアは複数のプロセッサを 1 個の CPU チップに搭載したもので、1 個のプロセッサでは性能が足りないので複数にしている、というところに源流がある

#### マルチプロセッサシステム

* 用語のおさらい
  * 処理性能を高めるために複数のプロセッサを使用するシステムは「マルチプロセッサシステム」と呼ばれる
  * 「プロセッサコア」とは、プロセッサの内部で独立して機能する演算・制御装置のこと。2 つ以上のコアが 1 つのパッケージに集積したプロセッサを「マルチコアプロセッサ」という
  * 1 つのコンピュータに複数のプロセッサを使用するシステムを「マルチソケットシステム」と呼ぶ
* マルチコアプロセッサの構造
  * 3 次キャッシュ以下のメモリコントローラや外部インタフェースとそれらの間をつなぐスイッチを、全部のプロセッサコアで共有する
  * マルチスレッドプロセッサでは 2 スレッド化しても数％しか面積が増えないが、マルチコアプロセッサでは 2 倍の面積が必要になる
* キャッシュコヒーレンシ制御
  * マルチプロセッサの場合は、キャッシュ感の整合性を取るためにキャッシュコヒーレンシ制御が必要になる
  * MSI プロトコルという方法が一番基本的。Modified / Shared / Invalid の 3 つの状態を持つ
  * 他のプロセッサに何が入っているか覗き込むことを「スヌープ」という
  * MESI プロトコル / MOSI プロトコル / MOESI プロトコル
* マルチソケットシステム
  * マルチソケットシステムでは、それぞれのプロセッサにキャッシュがある
  * 1 次、2 次キャッシュに格納されているキャッシュラインは必ず LLC（Last Level Cache）にも格納されている場合、LLC はインクルージョンキャッシュという
    * 格納が保証されない場合は、ノンインクルージョンキャッシュという
  * コモンバスではなく、より高速に動作させられる HyperTransport / QPI を利用するようになっている。これらはチップ間を Point-to-Point 接続する
* マルチプロセッサシステムの性能向上
  * キャッシュラインの転送は同一チップで 10〜数十サイクル、別チップのコア間で 100 サイクルかそれ以上かかるので、大きく性能が低下する要因になる
  * 多数のコアを使って並列にできる処理と、並列化ができず 1 つのコアで処理する処理がある。後者の時間はコア数に無関係に一定数あるので、並列処理の効果は飽和するというのがアダムールの法則
  * 複数のプロセッサに処理を分担させた時、プロセッサごとに仕事量が異なり、処理時間がばらつく状況をロードインバランス（Load Imbalance）という
* 共有メモリシステムと分散メモリシステム
  * すべてのプロセッサが同じ物理メモリ空間を共有するシステムを「共有メモリシステム」という
  * 「分散メモリシステム」はプロセッサグループごとに独立のメモリ空間を持つ
    * Web サーバのような用途では、分散メモリシステムでもオーバーヘッドは問題にならない。それぞれのアクセスの処理は独立しているので、データの分割や通信はほとんど不要であるため
  * Google のデータセンターなどは、小規模な共有メモリシステムを集めて、大規模な分散メモリシステムを作っている。このようなシステムをクラスタシステムという

### 6章: プロセッサ周辺技術

#### メインメモリ技術

* メインメモリの歴史のおさらい
  * 世界初の電子コンピュータであった ABC ではダイナミックメモリが使われており、その後様々なメモリテクノロジーが開発されたが、現在では再びダイナミックメモリが主流になっている
  * プロセッサ内部のキャッシュメモリでは高速動作が可能な SRAM（Static Random Access Memory）が使われる
    * 参考: [DRAMとSRAM ：一口メモ](http://mh.rgr.jp/memo/sc0055.htm)
  * DRAM（Dynamic Random Access Memory）はキャパシタ（コンデンサ、蓄電器）に蓄えた電荷で 1/0 の情報を記憶し、同じチップ面積なら SRAM の 8 倍程度の情報を記憶できる
* DRAM メモリの動作原理
  * DRAM 記憶セルのスイッチトランジスタは、ごくわずかだがオフ状態でも「漏れ電流」が流れる
    * このため、DRAM では各行を 32〜64 ミリ秒に 1 回は読み出しを行い、キャパシタの電荷を元の状態に戻す必要がある。これをリフレッシュという
  * プロセッサのキャッシュなどに使われるメモリは、電気回路的に情報を保持している。電源が入っている限りは静止した状態で情報を保持できるので、SRAM と呼ばれる
  * DRAM のリフレッシュ中は通常の Read / Write アクセスができないが、SRAM はリフレッシュ動作を必要としないので常にアクセスできる
  * SRAM セルのサイズは DRAM の記憶セルの 5〜10 倍の面積なので、その分ビット単価が高くなってしまう。そのため大容量を必要とするメインメモリでは DRAM が用いられる
* DRAM チップとメモリ DIMM
  * DIMM（Dual Inline Memory Module）は 8 ビット幅の DRAM チップを 8 個搭載し、アドレスやコマンド信号は全チップに同じ信号が伝わるように結線したもの
* プロセッサと DIMM の接続
  * プロセッサの性能あたりのメモリバンド幅は減少する傾向にある（プロセッサの性能向上のペースが早い）
  * これを補うため、DIMM を接続するポートを 2 ポート設けて、2 枚の DIMM に並列にアクセスできるようにするのが一般的
* メモリシステムのエラー対策
  * アルファ線や中性子などがシリコンに当たると誤動作を起こしてしまい、放射線によるエラーは故障よりも高い頻度で発生する
  * グループの中の 1 の数を偶数（または奇数）にして、エラーを検出するやり方を「パリティチェック」という
    * 1 の数をあわせるために追加するビットを「チェックビット」という
  * ECC（Error Correcting Code）を使うと、エラーしたビット位置も特定できる

#### 入出力装置の接続

* PCI バス（Peripheral Component Interface bus）と PCI Express
  * PCI バスは各種制御信号を持った、コモンバス構造のバス。ここからより高速の伝送を行うために、1 対 1 接続としたのが PCI Express
  * PCI Express は送信用と受信用で別々に信号線を持っているので、送信と受信を並行して行える
  * PCI バスと PCI Express は全く異なる技術を使っているが、ソフトウェアからは全く同じに見えるように作られている

### 7章: GPGPU と超並列処理

#### GPGPU の仕組み

* 3D グラフィックスと GPU（Graphics Processing Unit）
  * GPU は 3D グラフィックスなどの画像描写を行う際に必要となる計算処理に特化したプロセッサ
  * 3D グラフィックスを扱うゲームの場合、ゲーム用の入力デバイスの読み取りやゲーム全体の進行は CPU、3D オブジェクトの表示処理を GPU が担当する
* GPGPU（General Purpose GPU）
  * GPU をグラフィックス処理以外の科学技術計算に使用して、計算を高速化しようという動き
  * GPGPU プログラミングでは、データをどのメモリに置くか明示する必要がある
  * 汎用プロセッサの場合は、各スレッドに専用のレジスタを用意するが、GPGPU では 1 つのレジスタを並列に実行するすべてのレジスタで分割使用する
* CPU と GPGPU の大きな違い
  * 超並列 SIMD 処理による強力な演算能力（CPU の 10 倍かそれ以上）
  * 巨大マルチスレッドプロセッサ（GPGPU は千スレッド以上を並列に実行する）
  * CPU のマルチスレッドはそれぞれ別々の命令を実行できるが、GPGPU だと 1 つの SM（Stream Multiprocessor）で動くスレッドは、すべて同じ命令列を実行する

#### GPGPU プログラミング

* GPGPU プログラミングの今
  * NVIDIA の CUDA。OpenCL という GPGPU のプログラミングシステムの標準
* GPGPU の性能を引き出すには
  * データ転送やプログラム起動のオーバーヘッドを減らす
  * 多数の SIMD ユニットを高い効率で動かす
    * ワープ内の全スレッドを有効に動かす / 演算あたりのメモリアクセスを減らす / 同期回数を減らすなど

### 8章: 今後のプロセッサはどうなっていくのか？

* 電源電圧が下げ止まり、消費電力が重要な設計パラメータに
  * クロック周波数の向上ではなく、複数のプロセッサを作るマルチコアに方向転換
* 増加するトランジスタをうまく使うには 2 つのアプローチがある
  * マルチコア化の流れ / 周辺機能の取り込み
* より高信頼で安全なプロセッサ設計
