コンテキストスイッチを効果的に理解する方法
CPUの世界を掘り下げてみると、CPUが複数のタスクを効率よくこなす様子は実に驚くべきものです。初期のCPUは直線的な処理に特化していました。一見シンプルに聞こえますが、RAM、あるいはもっとひどい場合はハードドライブからのデータを待たなければならなかったため、速度は著しく制限されていました。CPUがアイドル状態のまま、瞬時に返ってこないデータを待っているのをただ見ているだけだったのです。ハードドライブが絡むとどうなるでしょうか?そう、RAMはフェラーリのようで、ハードドライブは自転車のようです。そうなると、システムはさらに遅くなります。
ありがたいことに、今日のプロセッサはただのアヒルではありません。アウトオブオーダー実行やマルチスレッドなど、様々な巧妙な処理を実行できます。アウトオブオーダーとは、CPUが先読みして命令を並べ替え、待機状態を保てるようにすることを意味します。マルチスレッドとは、複数のスレッドを実行できるため、技術的には2つの処理を同時に実行できないにもかかわらず、多くの処理が同時に行われているように見えることを意味します。裏では、すべてのコアを常にビジー状態に保つために、スレッド間で高速に切り替えが行われています。これをコンテキストスイッチと呼びます。正直なところ、これらがいかに速く行われるかは驚くべきものです。ほとんどのユーザーは短い休止に気づきませんが、これらの切り替えはバックグラウンドで絶えず行われているのです。
コンテキストスイッチはどのように機能しますか?
ここで魔法のようなことが起こります。設定によっては、多少の混乱が生じるかもしれません。基本的に、CPUは以前のスレッドの状態を保存して、後で中断したところから再開できるようにする必要があります。つまり、レジスタ値、プログラムカウンタなど、すべての重要な情報をプロセス制御ブロックまたはスイッチフレームと呼ばれるデータ構造に保存するということです。Windowsでは、タスクマネージャーを開いてスレッド情報などの詳細を確認することで、この動作を確認できる場合がありますが、通常は裏で自動的に処理されます。Linuxでは、htopや`top`などのツールを使うとスレッドの状態が表示され、内部で何が起こっているかを理解するのに役立ちます。
古いスレッドが安全に保存されると、CPUは次のスレッドを選択します。通常、スケジューラはキュー(実行可能なタスクの列と考えてください)からスレッドを1つ選択するか、割り込み(何かが完了した、または注意が必要であることを示すハードウェア信号)によって新しいスレッドを促します。この新しいスレッドのデータはCPUレジスタにロードされ、スイッチを切り替えるかのように動作します。その後、そのスレッドは中断したところからすぐに処理を再開します。ユーザーにとってはシームレスに見えますが、実際には超高速に処理されています。
パフォーマンスへの影響
さて、ここで問題があります。コンテキストスイッチが発生するたびに、多少の時間がかかります。最近のメモリはかなり高速なので、それほど大きなコストではありませんが、高性能環境では問題になる程度です。切り替えが発生すると、CPUのキャッシュとバッファ(いわゆるスピードブースター)は新しいスレッドに必要なデータを保持できなくなり、キャッシュミスが発生します。同じプロセス内でデータを共有すればこのロスは最小限に抑えられますが、異なるプロセス間や無関係なスレッド間で切り替えるとなるとどうでしょうか?そう、そうなるとキャッシュミスとTLBフラッシュが増え、さらに速度が低下します。環境によっては、顕著なラグや遅延が発生することもあります。
もう一つ奇妙な点があります。ハードウェアでもコンテキストスイッチは可能ですが、ほとんどのOSはソフトウェアによるコンテキストスイッチを好みます。なぜなら、ソフトウェアの方が保存と復元の判断がスマートだからです。ハードウェアは何が重要か判断できません。つまり、関連性に関わらずすべてのレジスタを保存するという、いわば「スレッジハンマー」のような働きをします。そこでOSが介入し、実際の保存と復元を行います。これには浮動小数点データなど、ハードウェアベースのコンテキストスイッチでは省略される可能性のあるものも含まれます。だからこそ、ソフトウェアによるコンテキストスイッチが主流なのです。全体的には効率的ですが、それでもパフォーマンスの低下は避けられません。
結論
結局のところ、コンテキストスイッチはマルチタスクの基本的な要素であり、CPUがタスクを落とすことなく複数のスレッドを処理できるようにするものです。コンテキストスイッチは、現在のスレッドの状態を保存し、次のスレッドを読み込む処理です。この処理は超高速ですが、それでもパフォーマンスを多少犠牲にします。特に異なるプロセス間や高負荷のワークロード間で頻繁に切り替えを行う場合、こうしたわずかな遅延が積み重なっていきます。とはいえ、ゲームからブラウザ内でのデータ処理まで、あらゆる処理をこなせる最新のマルチコアCPUを持つには、ある程度のコストがかかります。