第3章:奥深い低レイヤの数字を探る 株式会社アリエル・ネットワーク 袖山 剛 (Takeshi Sodeyama) sodeyama@ariel-networks.com http://d.hatena.ne.jp/sodex/ 1. はじめに 3章では、普段触れる事の少ない低レイヤの数字をハ ードウェアやカーネルといった観点から見ていくことに します。基本的には普段皆さんがお使いのIBM PC/AT互換 機(以下PCと呼びます)のアーキテクチャを元に話を進 めていきます。 2. 周波数とは 現代のコンピュータにおいて、最も重要な部品は何か と聞かれてCPUと答える人は多いかと思います。量販店 やCPUメーカーのCMなどで「クロック周波数3GHz」「Dua l Core」「キャッシュxMB」などというキーワードを元 に新技術の宣伝が盛んにされていますが、その中でも一 際強調されてきた周波数とは何なのでしょうか。 PCとは離れてちょっと一般的な話をすると、図1のよ うに波のような周期性のある状態が、固定観測点で1秒 間に観測できる回数の事を周波数と呼びます。噛み砕い た例で言うと、ピアノの練習でリズムを取るために使用 されるメトロノームなどがあります。メトロノームは規 則正しく左右に動いており、1秒の間にに針が左右に揺 れる回数が周波数となります。 PCにおける周波数とは、CPUやその周辺のコントローラ などの電子回路に入力させる電圧の高低のセットが1秒 間に発生する回数の事になります。図2のように電圧の 高低はおおよそ一定で、この電圧の高低の変化が1回発 生すると回路全体が同期を取り処理を行います。PCでは この周波数の事をクロック周波数や単にクロックと呼ん だりします。 2.1 水晶発振子 ではPC内部で周波数はどのようにして生成されている のでしょうか。CPU、メモリ、I/Oコントローラなど様々 電子部品がそれぞれ別々のクロック周波数の入力を必要 としますが、その発生源は実は一つのみです。水晶発振 子と呼ばれる素子がPC内部に一つ存在し、それが全ての 電子部品に対してクロックを提供しています。水晶には 電圧を掛けると振動する特性がありそれを利用して周波 数を生み出しています。非常に安価で高精度の周波数が 出力出来るため重宝されているようで、実際PC内部の部 品のみならず腕時計など様々な場面で使用されておりま す。 ところで、PCで使われている水晶発振子の周波数は固 定値が使われており、図2のようなクロックパルスを生 成する発振回路と呼ばれる回路により出力され、そのク ロック周波数は14.31818MHzとなっています。この固定値 だけではPC内部の様々なクロック周波数に対応出来ない ので変換する必要がありますが、その役割を担っている のがPLLクロックジェネレータという回路になります。 このクロックジェネレータは14.31818MHzという周波数 をベースクロック(別名ではシステムクロックまたはF SBとも呼びます)と呼ばれるPCの基本となるクロック 周波数に変換しています。 3. ベースクロックからCPUへ ベースクロックの周波数は固定値ではなく、規格によ って様々な種類があり現在では800MHz、1066MHzなどの 値が主流となっています。図3ではPLLクロックジェネ レータからCPUに伸びているバス(バスとはCPUやメモリ といった各回路を繋ぐ経路で、この経路を通じてデータ の送受信を行います)の周波数がベースクロックになり ます。 @@@@@(1)↑「バス」につきまして、このあとの ご解説中にも出てきたようでしたので、できれば 「バス(●●)」あるいは脚注などで、補足のご解説 をお願いできますとありがたいです @@@@@ここまで ※上記文中に追記しました。 図3からわかるように、CPUは独自に クロックを変換するクロックタブラと呼ばれる回路持 っており、ベースクロックを整数倍か半整数倍する事 ができます。例えば800MHzのベースクロックをクロッ クタブラが4倍にすれば3.2GHzといったCPU周波数が得 られます。では、このベースクロックを10倍にすれば 8GHzでCPUが動作するかというとそうはなりません。 3GHzという高周波領域では、CPUなどの電子回路内部で 回路全般に伝わる電圧の変化の速度は電流の速度では なく、電磁波の速度すなわち光の速度となっています。 光の速度は有限で秒速30万kmなので3GHzの1周期分( 約0.0000000003秒)の時間に進める距離は約10cmとな ります。高周波回路のシビアな条件では回路内の最大 の距離をこの10cmよりだいぶ短くする必要があり、ま た高周波における発熱の問題のためCPUのクロック周波 数は簡単には上げ難い状況となってきています。 4. その他のクロック周波数 CPUだけだはなく、メモリにもクロック周波数が必要 で、その値は基本的にベースクロックに準拠しています 。ただし、最近ではダブルレイトや先読み処理といった 特殊な処理がされており、メモリクロックの数倍でのア クセスが可能になってきています。その他にPCIバスと いった汎用拡張デバイス用に33MHzからその数倍が与え られていて、FDCやシリアルコントローラ、パラレルポ ートといったデバイスが納められているSuper I/Oと呼 ばれるチップに対しては24MHzというクロック周波数が 与えられています。またグラフィクスカード用にAGPと いった専用バスに66MHzというクロック周波数が与えら れているPCもあります。 5. CPU内部の動作 さてPC内部の各デバイスへ与える周波数の説明はこの 辺にしておき、CPU内部の話にうつりましょう。CPUはA T互換機で最も普及しているIntelのx86(エックスハチ ロク)アーキテクチャを前提とします。 CPU内部にはレジスタと呼ばれる一種のメモリがあり CPUに何らかの計算をさせる時には必ずこのレジスタ経 由で行われます。レジスタはCPUの回路内部に存在する ため、動作周波数はCPUと同等となり、図4のようにレ ジスタには一般レジスタ群、システムレジスタ郡、浮動 小数点レジスタ郡、デバッグレジスタ郡などがあり、ソ フトウェアのレイヤーで何らかの処理を行う事はこのレ ジスタ郡を操作する事とほぼ同義です(他にはI/Oの操 作があります)。このレジスタへの操作を抽象化してい るのがカーネルであり、GNU Cライブラリであり、Java VMであり、その上のフレームワークなのです。 @@@@@(2)↑本文ではないのですが、図4の図中の 「EAX」などの用語につきまして、図の外に用語注釈 をご追記いただくことは可能でしょうか。主要なもの だけでもかまいませんので、ご検討いただけますと ありがたいです @@@@@ここまで ※用語注釈として以下のものをお願いします。 EAX,EBX,ECX,EDX,ESI,EDIは汎用レジスタと呼ばれ、 一般的な演算時に使用します。 CS,DS,ES,SS,FS,GSはセグメントレジスタと呼ばれ、 メモリ空間の管理に使用します。 EIPはインストラクションポインタと呼ばれ、 現在実行中のプログラムのメモリ上の位置を指します。 EFLAGSはフラグレジスタと呼ばれ、CPUの状態を保持 しています。 EBPはベースポインタと呼ばれ、メモリ上のベースアド レスとして使用します。 ESPはスタックポインタと呼ばれ、プログラムのスタッ ク領域のメモリ上の位置を指します。 5.1 CPUへの1命令 ではCPUに対して与えられた周波数、例えば3GHzだと 、CPUは「a++;」のような演算を一秒間に3000000000回 しているかというと、してはいません。高級言語の演算 「a++;」に近いアセンブリ言語の演算「add eax, 1」を 例にとって処理を追ってみます。また、この部分はx86 アーキテクチャの中でも世代が異なると全く処理が異 なるのでi486、pentiumを元に話を進めます。 「add eax, 1」という命令はeaxというレジスタに対 して、元々入っていた値に1を加えるという命令です。 メモリ上(命令キャッシュ上)には現在実行中のプロ グラムが読み込まれており、それは「add eax, 1」を CPUが理解出来る機械語の形になっています。その機械 語をCPU上に読み込むのに1クロックかかります。この 機械語にはeaxレジスタに何かを加えるという命令部分 と「1」という即値の部分が存在し、Pentium CPUでは これを分解し解釈するのに2クロックほど使います。 ちなみにこの部分の処理の事をデコードと呼びます。 次に実際の命令である演算処理のの実行を行うのに1 クロック使います。最後にeaxレジスタに結果を書き込 むのに1クロック使い、全部で5クロック使います。 5.2 パイプライン このように一つの演算をするのに数クロック掛かり ますが、図5のように現代のCPUではパイプラインとい う技術によりこの5クロックかかる命令を並列に同時 実行する事が出来るようになっています。しかし、分 岐命令のように、ある命令が先行している命令の結果 に依存する場合、先行の命令がレジスタに結果を書き 込む前に後行の命令がレジスタから命令を呼び出すと 結果が異なってしまうので、その処理を途中で止めて 一からやリ直す必要が出てきます。予め結果を予測し て次の分岐を決め打ちで実行する機能がCPUには存在 しますがこれはソフトウェアレイヤでの制御は出来ま せん。このように機械語レベルでの1命令には、1ク ロックより多いクロック数が要求されます。 5.3 メモリ・レイテンシ 近年の高クロックで動作するCPUに対して、メモリ の動作クロックはかなり遅くなっており、その差は 中々縮みません。しかもその他の遅延要因を考えると CPU内部での処理に比べてメインメモリへのアクセス は2桁のオーダーで遅くなります。それを緩和する方 向として導入されたのがCPUの多段キャッシュです。 5.4 CPUキャッシュ キャッシュは1次、2次、場合によっては3次と複 数あり次数が増えると図6のように容量は増えていき ますが、その分レイテンシが増大していきます。頻繁 に使用されるデータを出来るだけCPUに近いキャッシ ュに置く必要がありますが、上位キャッシュでヒット しなかったデータは下位キャッシュから取得する必要 がありその分遅くなります。このように下位キャッシ ュからデータが必要になる事をキャッシュミスが発生 するなどと呼びます。 CPUにより異なりますが、キャッシュの動作クロック はおおよそ1次キャッシュがCPUと同等、2次キャッシ ュが同等もしくは半分程度といった値になっています。 また、キャッシュには種類があり、頻繁に使用するデ ータが格納されるデータキャッシュや命令自体を格納 する命令キャッシュなどがあります。 6. ディスク ハードディスクは半永続的にデータを保存したい場 合やメモリが足りないときの一時保管場所として使用 され、UltraDMAという規格やシリアルATAという規格 などでPCと接続されています。これらの規格ではシス テムバスを使用しているのでディスクとメモリ間はシ ステムクロックでデータが送受信されます。 @@@@@(3)↑「UltraDMAという規格やシリアル ATAという規格」という部分につきまして、たとえば 「UltraDMAやシリアルATAという規格(転送モード)」 と表現を変えてしまうと変になってしまいますでしょうか @@@@@ここまで ※その表現でお願いします。 ディスクはCPUやメモリといった電子デバイスとは 異なる部分が多く、構造を簡単に説明しておきます。 図7のようなプラッタという円版が軸に対して複数枚 付いており、その円板を上から見るとトラック(trac k)と呼ばれる同心円に分割されます。各トラックは 図8のような円盤の外側から番号が振られ、それをト ラック番号と呼びます。トラックはさらにセクタと呼 ばれる単位に円弧に分割され、PCでは1セクタが512 バイトとなっています。読み取りはこのセクタ単位で 行われるのですが、円板は高速で回転しており、円板 間に挿入されるヘッドが移動して任意のセクタからデ ータを読みとっています。 このようにハードディスクではヘッドの移動のよう な処理が入るため、他の電子部品に比べてアクセス速 度が桁違いに遅くなります。 7. ページング ここでディスクの重要な使われかたの一つを説明し ておきます。カーネルは、各プロセスが全メモリ空間 を専有しているように見せかけるために、物理メモリ をリニアアドレスというアドレスに変換しています。 リニアアドレスはページという4KBのサイズで管理さ れており、実在する物理アドレスに割り当てられます が、全てのリニアアドレスを物理アドレスでカバー出 来るわけではありません。仮想記憶が有効になってい る場合、物理アドレスが割り当てられていない領域の 値は実際にはディスクに保存されており、アクセスが 発生した時点でページフォルトという割り込みが発生 しメモリ上にロードされます。これをページインと呼 びます。その際にあまり使われていないメモリ領域が ハードディスクに書き込まれますが、これをページア ウトと呼びます。 このように頻繁にアクセスが発生するアドレス領域 はメモリ上でのみで処理が行われますが、アクセスの 発生するアドレス領域が拡散している場合はページフ ォルトが頻発し非常に速度が落ちます。 8. 割り込み カーネルに関わる周波数の話などをしていきますが、 その前にカーネルとハードの橋渡し的機能である「割り 込み」について話をしておきます。割り込みにはソフト ウェア割り込みとハードウェア割り込みの2種類存在し、 ハードウェア割り込みとは図9下段のように、ハード側で 何らかのイベントが発生した際、カーネルにその事を知ら せるためにCPUが持つ機能で強制的に発生します。例えば キーボードからのキー入力にこの機能を使用します。  ソフトウェア割り込みとはソフトウェア側で任意のタイ ミングで発生さる事が出来る割り込みで、システムコール のようにユーザープロセスがカーネル内部の処理を必要と する場合などに発生させます。 また、割り込みが発生するとその時点の処理を一時止め、 その処理に戻るためのメモリ番地(戻り番地)などをスタッ クに入れ、割り込み用の処理を開始します。 @@@@@(4)↑「割り込み」につきまして、用語の ご解説を少しご追記いただけましたらありがたいです。 合わせて「割り込み」と「ハードウェア割り込み」 は通常同じ、あるいは用語の使い分けがおこる場合が ありましたら、ご紹介いただけましたらありがたいです @@@@@ここまで ※上記文中に追記しました。 9. スケジューラ カーネル内部で正確な周波数が要求される機能として はスケジューラがあります。スケジューラとは各プロセ スをカーネルが強制的に切り替える時に行われる処理で あり、ハードウェア割り込みを利用して行っています。 PCには16bitタイマを内蔵した8254というPIT(プログラ マブル・インターバル・タイマ)相当の機能が内蔵され ていて、これが割り込みを発生するタイミングを生成し ています。PITには1.19318MHzというクロック周波数の 入力が必要ですが、これは水晶発振子が生成する14.31 818MHzを12周分して得られます。 この1.19318MHzという周波数は1周期で約83μsecで あり、プロセスを切り替える時間としては早すぎます。 そこでPITに対して適切な周波数を設定する必要があるの ですが、著者が趣味で開発しているSodexカーネルではこ れに100Hzを設定しており、LinuxカーネルではVersion 2.6から1000Hzをデフォルト設定しています(サーバーと して使用する場合には2.4からと同じ100Hzがデフォルト 設定されているようです)。 リスト1はSodexカーネル内部でのPIT設定と、そのPIT を利用したスケジューラ処理の一部のコードです。 pit_setcounterでPITの周波数をHZに設定し、i20h_do_ti merでPITからの割り込みが発生した時の処理をしていて、 最後にschdule関数でプロセスの切り換えを行っています 。 リスト1 Sodexカーネル スケジューラ割り込み処理 #define CLOCK_TICK_RATE 1193180 #define HZ 100 #define LATCH (CLOCK_TICK_RATE/HZ) void init_pit() { pit_setcounter(LATCH); } static void pit_setcounter(u_int16_t counter) { out8(PIT_CONTROL, 0x34); out8(PIT_COUNTER0, (counter&0xff)); out8(PIT_COUNTER0, ((counter>>8)&0xff)); } void i20h_do_timer(int is_usermode, u_int32_t iret_eip, u_int32_t iret_cs, u_int32_t iret_eflags, u_int32_t iret_esp, u_int32_t iret_ss, u_int32_t ebp) { out8(0x20, 0x60); save_process(is_usermode, iret_eip, iret_cs, iret_eflags, iret_esp, iret_ss, ebp); while (current->signal) { u_int32_t sig = maxsignal(current->signal)+1; switch (sig) { case SIGSTOP: case SIGTSTP: case SIGTTIN: case SIGTTOU: case SIGKILL: current->state = TASK_STOPPED; break; default: (current->sigactions[sig-1]->sa_handler)(sig-1); break; } current->signal &= (~(1<<(sig-1))); } int state = current->state; if (state == TASK_STOPPED) { _exit(); } else if (state == TASK_ZOMBIE) { // skip the current current = dlist_entry(current->run_list.next, struct task_struct, run_list); } else if (state == TASK_RUNNING) { } else { _kprintf("The number %x of task state is not implemented\n, state); } schedule(); } 10. シリアルポート 少し話はそれますが、少し前のPCでは欠かせなかったシ リアルポートについて話をしたいと思います。シリアル ポートはPCとその他デバイスとのデータ転送やネットワ ークに繋ぐモデムと接続する役割がありました。 PCにはシリアルポートを制御するNS16550というコン トローラ相当の機能がSuper I/Oチップに納められてお り、その入力周波数はSuper I/Oの周波数24MHzを13周分 して得られる1.8462MHzとなっています。この周波数を 使ってNS16550コントローラでLATCH等の設定をし、かつ てのネットユーザーにはおなじみの9600bpsや33600bps といった転送レートを生成しています。しかし1.8462M Hzという周波数ではこの転送レートに0.2%ほどの誤差 があり、本来64Kbpsとする所を57.6Kbpsとしたり128K bpsのところを115.2KbpsとするなどPC特有の転送レー トがありました。 11. 各デバイスのアクセス速度比較 今までPC内部の様々な動作を説明してきましたが 、最後にそれらのアクセス速度をlmbenchというベンチ マークソフトで計測した結果を記載しておきます。 @@@@@(5)↑「lmbench」につきまして、 「UNIX系OSで使用します」みたいな内容を追加いただけ ましたらありがたいです。その際、Windowsの方に向けに 補足情報のご提示が可能でしたらご紹介いただけますと とてもありがたいです @@@@@ここまで ※下記文中に追記しました。 http://www.bitmover.com/lmbench/ にて取得できます ので皆さんのPCでも計測してみてください。 (また、WindowsではCrystalMark 2004R2というベンチマ ークソフトで似たような測定が可能です) 著者の環境であるIntel Core Solo 1.06GHz, 2MB L2 キャッシュ、FSB 533MHzでOSはLinux 2.6.18のFedora Core 6にてテストしてみた結果を表1に記載しておき ます。 この結果を見ると、L1キャッシュが約3nsでアクセス出 来るのに対して、L2は4倍強、メインメモリは二桁のオ ーダー差がある約100nsの時間が掛かります。クロック 周波数から計算した1クロックの周期は約1nsですので L1キャッシュへのアクセスは3クロック、L2キャッシュ へは13クロック、メインメモリへは109クロックほど掛 かっているといえます。 また、アドレスが連続した配列のような変数にアクセス する場合L1キャッシュの範囲で収まりますが、ハッシュ のように不連続なアドレスにアクセスする場合はメイン メモリへのアクセスが発生し数十倍のロスが発生する可 能性がある事がわかります。プロセス切り換えには大体 3 microseconds掛かっていますが、切り換えが発生する タイミングが10msec(100Hz)である事を考えると十分小 さい事がわかります。 ディスクへのアクセスはFileのCreateでわかるように メインメモリよりさらに2桁遅くなります。ディスクの ヘッド移動があるようなケースではさらに2桁ほど遅く なります。 このように低レイヤに潜む数字を紐解き理解する事 によって、どの処理にどのくらい掛かるのかを大体把 握出来るようになり、上位レイヤでのパフォーマンス チューニング等に役立つ事があるかもしれません。 表1 各種アクセス(処理)速度比較表 アクセス対象(処理) アクセス時間(処理時間) L1 2.9150 nanoseconds L2 13.6 nanosecconds Main Memory 109.3 nanosecconds プロセス切り換え(2プロセス中) 2.79 microseconds プロセス切り換え(16プロセス中) 3.98 microseconds readシステムコール 0.8873 microseconds writeシステムコール 0.8435 microseconds openシステムコール 7.1981 microseconds ページフォルト 1.9917 microseconds 0KBファイル作成 21.7 microseconds 10KBファイル作成 68.4 microseconds Mmap Latency 1657.0 microseconds