命令オペコードをプログラムメモリから事前にフェッチすることをプリフェッチといい、プリフェッチ入力キュー(PIQ)を用いて実現されます。プリフェッチされた命令はキューに格納されます。オペコードの実行が必要になる前に事前にフェッチすることで、プロセッサ全体の効率が向上し、処理速度が加速します。プロセッサは、次の命令オペコードのためのメモリアクセス操作が完了するのを待つ必要がなくなります。このアーキテクチャは、Intel 8086マイクロプロセッサで広く採用されました。
パイプライン処理は、より高速で効率的なコンピューティングの必要性から、1960年代にコンピューティングアーキテクチャ設計の最前線に躍り出ました。パイプライン処理はより広範な概念であり、現代のほとんどのプロセッサは、命令を実行する数クロックサイクル前に命令をロードします。これは、マシンコードをメモリからプリフェッチ入力キューに 事前にロードすることによって実現されます。
この動作は、自己修正コードを実行でき、何らかの命令パイプラインを備えているフォン・ノイマン型コンピュータ(つまり、ハーバードアーキテクチャのコンピュータではない)にのみ適用されます。現代の高性能コンピュータのほぼすべてが、これら3つの要件を満たしています。[ 1 ]
通常、PIQのプリフェッチ動作はCPUのプログラミングモデルからは見えません。しかし、PIQの動作が可視化され、プログラマが考慮する必要がある状況もいくつか存在します。
x86プロセッサがリアルモードからプロテクトモードに、またはその逆へとモードを切り替える際には、PIQをフラッシュする必要があります。そうしないと、CPUはマシンコードを前回のモードで記述されたかのように変換し続けます。PIQがフラッシュされない場合、プロセッサはコードを誤って変換し、無効な命令例外を生成する可能性があります。
自己書き換えコードを実行する場合、実行位置の直前のプロセッサコードに変更があっても、プロセッサがコードを解釈する方法は変わらない可能性があります。これは、変更前のコードが既にPIQにロードされているためです。プロセッサは、RAMやキャッシュにある新しい変更後のコードではなく、PIQに既にロードされている古いバージョンのコードを実行するだけです。
PIQのこの動作は、コードがエミュレータ内で実行されているのか、実際のCPUハードウェア上で直接実行されているのかを判断するために使用できます。ほとんどのエミュレータは、おそらくこの動作をシミュレートすることはないでしょう。PIQサイズがゼロの場合(コードの変更は常にプロセッサの状態に即座に影響します)、コードがエミュレータ内で実行されているか、PIQにロードされたアドレスへの書き込み時にプロセッサがPIQを無効化しているかのいずれかであると推測できます。
電話回線の混雑を解消する解決策として、最初に待ち行列の概念を考案したのはA.K.アーラン(1878-1929)でした。さまざまな性能仕様に基づいて数学的に分析できるように、リアルタイムの待ち行列システムを近似的にシミュレートするために、さまざまな待ち行列モデルが提案されています。
待ち行列モデルは、ケンドールの記法を用いて表現することができる。
どこ:
プリフェッチ入力キューのようなアプリケーションでは、キュー機能の使用が限られているため、一般的にM/M/1モデルがよく用いられます。このモデルでは、マイクロプロセッサに応じて、ユーザーが実行ユニットの役割を担い、サーバがバスインターフェースユニットの役割を担います。
プロセッサは、メモリから命令をフェッチして実行することでプログラムを実行します。通常、プロセッサの実行速度はメモリへのアクセス速度よりもはるかに高速です。命令キューは、プロセッサが現在の命令を実行している間に、次の命令を別のバッファにプリフェッチするために使用されます。
4段階パイプラインでは、命令の実行速度は逐次実行の最大4倍になる可能性がある。[ 5 ]
プロセッサは通常、命令のフェッチと命令の実行のための2つの独立したユニットを備えています。[ 6 ] [ 7 ]
パイプラインアーキテクチャの実装は、バスインターフェースユニットと実行ユニットが独立している場合にのみ可能です。実行ユニットがデータバスやアドレスバスを使用しない命令をデコードまたは実行している間、バスインターフェースユニットはメモリから命令オペコードをフェッチします。
このプロセスは、アドレスを送信し、オペコードを読み取り、それをデコードして実行するよりもはるかに高速です。現在の命令がデコードまたは実行されている間に次の命令をフェッチすることをパイプライン処理と呼びます。[ 8 ]
8086プロセッサは6バイトのプリフェッチ命令パイプラインを備えているのに対し、8088は4バイトのプリフェッチを備えています。実行ユニットが現在の命令を実行している間、バスインターフェースユニットはメモリから最大6バイト(または4バイト)のオペコードを事前に読み取ります。キューの長さはシミュレーション研究に基づいて選択されました。[ 9 ]
実行ユニットが分岐命令(ジャンプ命令またはコール命令)に遭遇すると、例外が発生します。この場合、キュー全体をダンプし、命令ポインタが指す内容をメモリからフェッチする必要があります。
命令キュープリフェッチアルゴリズムを実装するプロセッサは、技術的に非常に高度です。このようなプロセッサのCPU設計レベルの複雑さは、通常のプロセッサよりもはるかに高くなります。これは主に、 BIUとEUという2つの独立したユニットを実装し、それぞれが独立して動作する必要があるためです。
これらのチップの複雑さが増すにつれて、コストも増加します。これらのプロセッサは、プリフェッチ入力キューを持たない同等のプロセッサに比べて、相対的に高価です。
しかし、これらの欠点はプロセッサの実行時間の改善によって大きく相殺される。8086プロセッサでプリフェッチ命令キューが導入されて以来、後継のすべてのプロセッサにこの機能が組み込まれている。
code_starts_here: mov bx , ahead mov word ptr cs :[ bx ], 9090h ahead: jmp near to_the_end ; その他のコードto_the_end:この自己書き換えプログラムは、 jmp to_the_end を2 つのNOP (エンコード値は0x9090 )で上書きします。jmp near to_the_endは 2 バイトのマシン コードにアセンブルされるため、2 つの NOP はこのジャンプのみを上書きし、他の部分は上書きしません。(つまり、ジャンプは何もしないコードに置き換えられます。)
ジャンプ命令のマシンコードは既にPIQに読み込まれており、おそらくプロセッサによって既に実行されているため(スーパースカラプロセッサは複数の命令を同時に実行しますが、後方互換性のために実行していないように「見せかけ」ます)、コードの変更は実行フローに影響を与えません。
これは、PIQのサイズを決定するNASM(構文自己修正型x86アセンブリ言語)アルゴリズムの例です。
code_starts_here: xor bx , bx ; ゼロレジスタ bx xor ax , ax ; ゼロレジスタ axmov dx , cs mov [ code_segment ], dx ; 下のジャンプで code_seg を「計算」します (edx もここにあります)around: cmp ax , 1 ; ax が変更されたかどうかを確認je found_size ; 0x90 = オペコード "nop" (NO oPereration) mov byte [ nop_field + bx ], 0x90 inc bxdb 0xEA ; 0xEA = オペコード "far jump" dw flush_queue ; オフセット (rm = "dw", pm = "dd") が続く必要がありますcode_segment: dw 0 ; そしてコードセグメント (上記で計算) flush_queue: ; 0x40 = オペコード "inc ax" (ax をインクリメント) mov byte [ nop_field + bx ], 0x40 nop_field: times 256 nop jmp around found_size: ; ; レジスタ bx には PIQ のサイズが格納されます; このコードは [[リアル モード]] および [[16 ビット保護モード]] 用ですが、[[32 ビット保護モード]] で実行するように簡単に変更できます。オフセットの "dw" を"dd" に変更するだけです。また、上部の dx を edx に変更する必要があります。 (dw および dx は 16 ビットアドレス指定、dd および edx は 32 ビットアドレス指定) ;このコードが基本的に行っていることは、実行フローを変更し、総当たりでPIQのサイズを決定することです。「目の前のコードをどれくらい遠くまで変更すれば、自分に影響を与えることができるのか?」近すぎる場合(既にPIQ内にある場合)、更新は効果がありません。十分に離れていれば、コードの変更がプログラムに影響を与え、プログラムはプロセッサのPIQのサイズを特定します。このコードがマルチタスクOSで実行されている場合、コンテキストスイッチによって誤った値が得られる可能性があります。