コンピュータのしくみ

INPUT · スライド

速い記憶と遅い記憶

01 / 12

速さと大きさは同時に手に入らない

コンピュータの中には、データを覚えておく部品が何種類も入っている。ぜんぶ「いちばん速いもの」にすれば気持ちいいけれど、そうはなっていない。速い部品はとても高くて、たくさん作れないからだよ。

記憶の部品には、どれにも同じ関係がある。

  • 速いものは小さく、高い
  • 大きいものは遅く、安い

これは技術が未熟だから起きていることではなくて、仕組みそのものから来る性質だよ。だからコンピュータの設計は「どれか1つを選ぶ」のではなく、性格の違う部品を組み合わせて、速さと大きさの両方をなんとか手に入れるという方向に進んだ。

その組み合わせ方が、このレッスンの主題になる。

02 / 12

記憶階層 ― 上は速く小さく、下は遅く大きい

部品を速い順に上から積んだ並びを 記憶階層 と呼ぶ。上に行くほど速くて小さく、下に行くほど遅くて大きい。

  • レジスタ … CPU の中にある作業台。いちばん速いが数十バイトしかない
  • キャッシュメモリ … CPU のすぐ隣。数 MB
  • 主記憶(メインメモリ) … いわゆるメモリ。数 GB
  • 補助記憶(SSD・磁気ディスク) … 電源を切っても消えない置き場。数 TB

上下で速さは何桁も違う。レジスタと補助記憶を比べると、体感で言えば「手元の紙」と「倉庫まで取りに行く」ほどの差があるよ。

データは基本的に隣どうしでやり取りする。CPU が補助記憶を直接読むのではなく、下から上へ順に運び上げていく形だよ。

レジスタ  最速 / 数十バイトキャッシュ  速い / 数MB主記憶(DRAM)  ふつう / 数GB補助記憶(SSD/HDD)  遅い / 数TB

03 / 12

なぜ階層でうまくいくのか ― 局所性

小さくて速い部品を上に置いても、そこに入りきらないデータは下から取ってくることになる。それなら意味がないのでは、と思うよね。ここで効いてくるのが 局所性 という性質だよ。

プログラムのデータの読み方は、まったくばらばらではない。実際には次の2つの偏りがある。

  • 時間的局所性 … いま使ったものは、すぐまた使われやすい
  • 空間的局所性 … 使ったもののが、次に使われやすい

くり返しの処理を思い浮かべると分かりやすい。同じ命令が何度も実行され、配列の要素は前から順にたどられる。つまりごく狭い範囲が集中的に使われるんだ。

だから「よく使うほんの一部だけ」を上の階層に置いておけば、ほとんどの読み書きが上だけで済んでしまう。小さくても効くのはこの偏りのおかげだよ。

時間的局所性  さっき使ったものを  また使う空間的局所性  使ったものの隣を  次に使う

04 / 12

キャッシュメモリの働き

キャッシュメモリ は、CPU と主記憶のあいだに挟む小さくて速いメモリだよ。仕事はひとつだけ — 主記憶から読み出したデータを手元に取っておいて、次に同じところを読むときは主記憶まで行かずに返す

手元にあって返せたことを ヒット、無くて主記憶まで行くことを ミス(ミスヒット)と呼ぶ。

ミスしたときの動きは覚えておこう。主記憶から、その周辺をブロックごとまとめてキャッシュへ読み込む。1バイトだけ持ってこないのは、空間的局所性があるので隣も使われる見込みが高いからだよ。

この入れ替えはハードウェアが勝手にやっている。プログラムが指示するわけでも、割込みが起きてソフトウェアが運ぶわけでもない。ここは試験でよく突かれるところだよ。

CPU がデータを要求キャッシュにある?  あり → ヒット        そのまま返す  なし → ミス        主記憶から        ブロック転送

05 / 12

実効アクセス時間 ― なぜ重み付け平均になるのか

キャッシュを付けると速くなる、では話が粗い。平均でどれくらいになるのかを数で出したいよね。それが 実効アクセス時間 だよ。

考え方は難しくない。読み書きは毎回、ヒットする場合と、ミスする場合の2通りしかない。そして

  • ヒットしたときにかかる時間は、キャッシュのアクセス時間
  • ミスしたときにかかる時間は、主記憶のアクセス時間

ヒット率が h なら、全体のうち h の割合がヒットで、残りの 1 - h がミスになる。それぞれの時間に、起きる割合を掛けて足すと平均が出る。これが重み付け平均になる理由だよ。式を暗記する必要はなくて、「起きる割合ぶんずつ足す」と思えばいい。

数字を入れてみよう。キャッシュ10ナノ秒、主記憶60ナノ秒、ヒット率0.9のとき、10 × 0.9 = 960 × 0.1 = 6、合わせて 15ナノ秒。60より、ずっと10寄りの値になっているね。

実効 = C×h + M×(1-h)C: キャッシュの時間M: 主記憶の時間h: ヒット率10×0.9 = 960×0.1 = 6      合計 15ns

06 / 12

逆に、ヒット率を求める

試験では逆向きに聞かれることのほうが多い。実効アクセス時間だけ与えられて、ヒット率を出させる形だよ。

やることは同じ式を h について解くだけ。さっきと同じ数字で、実効アクセス時間が15ナノ秒だったとしよう。

15 = 10h + 60(1 - h) から始めて、かっこを開いて 15 = 10h + 60 - 60hh をまとめると 15 = 60 - 50h、移項して 50h = 45、割って h = 0.9 だよ。

答えを出したら必ず向きを確かめること。実効15ナノ秒はキャッシュの10に近いので、ヒット率は高いはず。ここで 0.1 や 0.17 が出たら、掛ける相手を入れ違えている合図だよ。

15 = 10h + 60(1-h)15 = 10h + 60 - 60h15 = 60 - 50h50h = 45  h = 0.9

07 / 12

書き込みはどうする ― ライトスルーとライトバック

ここまでは読む話だった。書くときはもう一段の悩みがある。キャッシュだけ書きかえると、主記憶の中身が古いままになってしまうからだよ。

選び方は2つある。

  • ライトスルー … キャッシュと主記憶を同時に書きかえる。中身は常に一致しているが、書くたびに主記憶まで行くので遅い
  • ライトバック … いまはキャッシュだけ書きかえ、そのデータが追い出されるときに主記憶へ書き戻す。主記憶へ行く回数が減るので速いが、一時的に食い違う

ライトバックの狙いは、はっきり主記憶への書込み回数を減らすことだよ。書き戻しが要らなくなるわけではない — 追い出すときにちゃんと書いている。ここを取りちがえた選択肢がよく出るよ。

ライトスルー  CPU → キャッシュ      → 主記憶  (同時)ライトバック  CPU → キャッシュ  追い出すとき      → 主記憶

08 / 12

DRAM と SRAM ― 中身が違う2つのメモリ

主記憶とキャッシュは、実は別の作りのメモリを使っている。

DRAM は、コンデンサに電気をためた状態かどうかで1ビットを表す。作りが単純なのでたくさん詰められて安い。ただしためた電気は自然に抜けるので、リフレッシュ(周期的な再書込み)を続けないと中身が消える。安くて大きいので 主記憶 に使われる。

SRAM は、フリップフロップという回路で1ビットを保つ。電気が通っているあいだは保ち続けるのでリフレッシュが要らず、速い。ただし1ビットに回路を何個も使うので高くて大きくできない。速くて小さいので キャッシュメモリ に使われる。

覚え方は D はリフレッシュが要る(Dynamic)、S は要らない(Static)。どちらも電源を切れば消える揮発性だよ。

DRAM  コンデンサ      リフレッシュ要      安い / 主記憶SRAM  フリップフロップ      リフレッシュ不要      速い / キャッシュ

09 / 12

消えないメモリ ― ROM とフラッシュメモリ

DRAM も SRAM も電源を切れば消える。消えてほしくないものを入れておくのが ROM(読み出し専用メモリ)で、こちらは 不揮発性 だよ。

書きかえられるかどうかで種類が分かれる。

  • マスク ROM … 製造時に中身を焼き込む。あとから書きかえられない
  • PROM … 出荷後に一度だけ書き込める
  • EPROM … 紫外線を当てて消し、書き直せる
  • EEPROM … 電気で消して書き直せる

マスク ROM の値打ちは、書きかえられないこと自体にある。出荷したあとで中身を不正に書きかえられないので、機器に組み込むプログラムの置き場に向いているよ。

フラッシュメモリ は EEPROM の仲間で、ブロック単位で電気的に消して書き直す。USB メモリや SSD の中身がこれだよ。リフレッシュは要らないし、紫外線も使わない。

マスクROM 製造時に固定PROM    一度だけ書けるEPROM   紫外線で消すEEPROM  電気で消すフラッシュ ブロック単位

10 / 12

メモリインタリーブ ― 分けて同時に読む

主記憶そのものを速くする工夫もある。メモリインタリーブ だよ。

主記憶を バンク と呼ぶいくつかの独立したグループに分けて、連続するアドレスを別々のバンクに散らして置く。すると連続した領域を読むときに、複数のバンクを並列にアクセスできる。1つのバンクが応答を返すのを待っているあいだに、隣のバンクの読み出しを始められるわけだね。

名前が似た仕組みと並べて、何を分けているかで覚えよう。

  • メモリインタリーブ … 主記憶を分けて並列に読む
  • キャッシュメモリ … 速い小さなメモリを挟んで差を埋める
  • DMA … CPU を通さずに直接やり取りする

3つとも高速化の工夫なので、選択肢に並べて出される。「主記憶を複数のグループに分けて並列」と書いてあればインタリーブだよ。

バンク0 バンク1バンク2 バンク3  ↓ 同時に読む連続アドレスを分散配置

11 / 12

補助記憶 ― 磁気ディスクにかかる時間

階層のいちばん下、補助記憶を見よう。磁気ディスク(ハードディスク)は円盤を回してヘッドで読む機械なので、読み出しに物理的に動く時間がかかる。

アクセス時間は3つの足し算だよ。

  • シーク時間(位置決め時間)… ヘッドを目的のトラックまで動かす
  • 回転待ち時間 … 目的の場所が回ってくるのを待つ
  • 転送時間 … 実際にデータを読み出す

計算のかんどころは回転待ちだよ。1回転ぶん待つことも、待たずに済むこともあるので、平均は1回転の半分になる。6,000回転/分なら1回転は 60 ÷ 6000 秒 = 10ミリ秒、平均待ちは 5ミリ秒だね。

転送時間は データ量 ÷ 転送速度。1,000バイトを10Mバイト/秒で読むなら 0.1ミリ秒だよ。

SSD はフラッシュメモリなので回る部品がなく、シークも回転待ちも無い。だから速くて静かで衝撃に強いよ。

アクセス時間 =  シーク時間+ 回転待ち時間+ 転送時間6000回転/分→ 1回転 10ms→ 平均待ち 5ms

12 / 12

つなぎ口と DMA

最後に、装置をつなぐ口の話。信号線の使い方で2種類に分かれるよ。

  • シリアル … 1本の線で1ビットずつ順に送る
  • パラレル … 何本もの線で同時に送る

一見パラレルのほうが速そうだけれど、線を増やすと到着時刻がそろわなくなるので、高速にすると使いものにならない。今のインタフェースはほとんどシリアルだよ。USBBluetooth もシリアルで、USB はハブを使ってツリー状に機器をつなげる。USB 3.0 では「スーパースピード」と呼ばれる 5G ビット/秒の転送ができるよ。

もう1つ覚えておきたいのが DMA だよ。ふつう装置と主記憶のやり取りは CPU が仲立ちするけれど、それでは CPU が運搬役にかかりきりになる。DMA はCPU を通さずに、装置と主記憶が直接データをやり取りする仕組みで、そのあいだ CPU は別の仕事をしていられるよ。

シリアル … 1本ずつ順にパラレル … 何本も同時にUSB / Bluetooth  → シリアル