記憶の壁とは何かを正確に理解しようとしています。これまでにわかったことに基づいて、メモリバリア(例:mfence
)を使用して、メモリバリアの前から後および前から後への命令の順序変更を防止しています。
これは、使用中のメモリバリアの例です。
instruction 1
instruction 2
instruction 3
mfence
instruction 4
instruction 5
instruction 6
今私の質問です:mfence
命令は、CPUに命令を実行する順序を伝えるマーカーにすぎませんか?または、CPUが他の命令を実行するように実際に実行する命令です(例:mov
)。
CPUがコード内で遭遇するすべてのバイトシーケンスは、CPUが実行する命令です。他の種類の指示はありません。
インテルインストラクションセットリファレンス と特定のページ mfenceの場合 の両方でこれをはっきりと見ることができます。
MFENCE
MFENCE命令の前に発行されたすべてのメモリからの読み込み命令とメモリへの保存命令でシリアル化操作を実行します。このシリアル化操作により、プログラムの順序でMFENCE命令の前にあるすべてのロードおよびストア命令が、MFENCE命令に続くロードまたはストア命令の前にグローバルに表示されることが保証されます。MFENCE命令は、すべてのロードおよびストア命令、その他のMFENCE命令、LFENCEおよびSFENCE命令、およびシリアル化命令(CPUID命令など)に関して順序付けられています。 MFENCEは命令ストリームをシリアル化しません。弱く順序付けされたメモリタイプを使用すると、アウトオブオーダーの問題、投機的読み取り、書き込み結合、書き込み折りたたみなどの手法を通じて、より高いプロセッサパフォーマンスを実現できます。データの利用者がデータの順序が弱いことを認識または認識する程度は、アプリケーションによって異なり、このデータの作成者にはわからない場合があります。 MFENCE命令は、弱い順序の結果を生成するルーチンとそのデータを使用するルーチンの間でロードとストアの順序を確実にするパフォーマンス効率の高い方法を提供します。
プロセッサは、WB、WC、およびWTメモリタイプを使用するシステムメモリの領域から投機的にデータをフェッチおよびキャッシュできます。この投機的なフェッチはいつでも発生する可能性があり、命令の実行とは関係ありません。したがって、MFENCE命令の実行に関しては順序付けされていません。データは、MFENCE命令の実行の直前、最中、または後に投機的にキャッシュに入れることができます。
抜粋からわかるように、MFence
命令は単なるマーカーではなく、かなりの作業を行います。
mfence
がパイプラインのフローに与える影響について説明します。たとえば Skylake パイプラインを考えます。次の一連の指示を検討してください。
inst1
store1
inst2
load1
inst3
mfence
inst4
store2
load2
inst5
命令は、同じプログラム順序でuopsのシーケンスにデコードされます。次に、すべてのuopsが順番にスケジューラに渡されます。通常、フェンスなしでは、すべてのuopsが順不同で実行されます。ただし、スケジューラがmfence
uopを受信した場合、すべての上流のメモリuopsがグローバルに見えるようになるまで(つまり、ストアが廃止されるまで)、mfence
の下流のメモリuopsが実行されないようにする必要があります。負荷は少なくとも完了しています)。これは、アクセスされる領域のメモリタイプに関係なく、すべてのメモリアクセスに適用されます。これは、バッファーが排出されるまで、スケジューラーがダウンストリームストアまたはロードuopsをそれぞれストアバッファーまたはロードバッファーに発行しないようにするか、ダウンストリームストアまたはロードuopsを発行して、それらを区別できるようにマークすることによって実現できますバッファ内のすべての既存のメモリuops。フェンスの上または下にあるすべての非メモリuopsは、アウトオブオーダーで実行できます。この例では、一度store1
引退してload1
が完了すると(データを受信して内部レジスタに保持することにより)、mfence
命令の実行が完了したと見なされます。 mfence
はバックエンド(ROBまたはRS)のリソースを占有する場合としない場合があり、複数のuopに変換される可能性があると思います。
Intelは、1999年にmfence
の動作を説明する 特許 を提出しています。これは非常に古い特許であるため、実装が変更されたり、プロセッサーによって異なる場合があります。ここで特許を要約します。 mfence
は3つのuopsにデコードされます。残念ながら、これらのuopsが何に使用されているのかは正確には明らかではありません。その後、エントリーはリザベーションステーションから割り当てられ、uopsを保持するために割り当てられます。また、ロードバッファとストアバッファからも割り当てられます。これは、ロードバッファーが実際のロードリクエストまたはフェンス(基本的には偽のロードリクエスト)のエントリを保持できることを意味します。同様に、ストアバッファーは、真のストアリクエストとフェンスのエントリを保持できます。 mfence
uopは、以前のすべてのロードまたはストアuops(それぞれのバッファー内)が廃止されるまでディスパッチされません。これが発生すると、mfence
uop自体がメモリ要求としてL1キャッシュコントローラに送信されます。コントローラーは、以前のすべての要求が完了したかどうかを確認します。その場合、それは単にNOPとして扱われ、uopはバッファーからdeallcoatされます。それ以外の場合、キャッシュコントローラはmfence
uopを拒否します。
mfenceは命令です。
Linuxで取得するには:
1 /ファイルmfence.cを書き込む
#include <stdio.h>
int main(){
printf("Disass me\n");
asm volatile ("mfence" ::: "memory");
return 0;
}
2 /コンパイル
gcc mfence.c mfence
3 /分解する
objdump -d mfence | grep -A 10 "<main>:"
000000000000063a <main>:
63a: 55 Push %rbp
63b: 48 89 e5 mov %rsp,%rbp
63e: 48 8d 3d 9f 00 00 00 lea 0x9f(%rip),%rdi # 6e4 <_IO_stdin_used+0x4>
645: e8 c6 fe ff ff callq 510 <puts@plt>
64a: 0f ae f0 mfence
64d: b8 00 00 00 00 mov $0x0,%eax
652: 5d pop %rbp
653: c3 retq
654: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
65b: 00 00 00
4 /行64aでmfence
が(3ビット)命令であることを確認します(0f ae f0)
つまり、それはCPU命令です(mov
など):プロセッサは、前の命令に到達する前に、それをデコードする必要があります。
たとえば、0f ae f0
はアドレスに表示されるため、CPUはアドレスをメーカーとして使用できません。
最後に、これは古い学校の命令であり、パイプラインの実行ポイントで、次の命令を実行する前に、パイプラインのメモリアクセスをさらに同期します。
注:Windowsでは、マクロ_ReadWriteBarrier
を使用してmfenceを作成します
あなたの質問は間違った仮定をしています。 MFENCEは命令の並べ替えを妨げません(強調表示された引用を参照)。たとえば、レジスタでのみ動作する1000命令のストリームがあり、MFENCE命令が中央に配置されている場合、CPUがこれらの命令を並べ替える方法には影響しません。
MFENCE命令は、すべてのロードおよびストア命令、その他のMFENCE命令、LFENCEおよびSFENCE命令、およびシリアル化命令(CPUID命令など)に関して順序付けられています。 MFENCEは命令ストリームをシリアル化しません。
代わりに、MFENCE命令は、キャッシュとメインメモリへのロードとストアの並べ替えを防ぎます。