パフォーマンスの監視にtscを使用しようとしており、命令の並べ替えを防止したいとします。
これらは私たちのオプションです:
1: rdtscp
シリアル化呼び出しです。rdtscpへの呼び出しの前後の並べ替えを防ぎます。
__asm__ __volatile__("rdtscp; " // serializing read of tsc
"shl $32,%%rdx; " // shift higher 32 bits stored in rdx up
"or %%rdx,%%rax" // and or onto rax
: "=a"(tsc) // output to tsc variable
:
: "%rcx", "%rdx"); // rcx and rdx are clobbered
ただし、rdtscp
新しいCPUでのみ使用できます。したがって、この場合はを使用する必要がありますrdtsc
。ただしrdtsc
、シリアル化されていないため、単独で使用してもCPUによる並べ替えが妨げられることはありません。
したがって、次の2つのオプションのいずれかを使用して、並べ替えを防ぐことができます。
2:cpuid
これはとの呼び出しrdtsc
です。cpuid
シリアル化呼び出しです。
volatile int dont_remove __attribute__((unused)); // volatile to stop optimizing
unsigned tmp;
__cpuid(0, tmp, tmp, tmp, tmp); // cpuid is a serialising call
dont_remove = tmp; // prevent optimizing out cpuid
__asm__ __volatile__("rdtsc; " // read of tsc
"shl $32,%%rdx; " // shift higher 32 bits stored in rdx up
"or %%rdx,%%rax" // and or onto rax
: "=a"(tsc) // output to tsc
:
: "%rcx", "%rdx"); // rcx and rdx are clobbered
3:これはclobberリスト内のrdtsc
withの呼び出しであり、並べ替えを防ぎますmemory
__asm__ __volatile__("rdtsc; " // read of tsc
"shl $32,%%rdx; " // shift higher 32 bits stored in rdx up
"or %%rdx,%%rax" // and or onto rax
: "=a"(tsc) // output to tsc
:
: "%rcx", "%rdx", "memory"); // rcx and rdx are clobbered
// memory to prevent reordering
3番目のオプションについての私の理解は次のとおりです。
呼び出しを行うと__volatile__
、オプティマイザがasmを削除したり、asmの結果を必要とする(または入力を変更する)可能性のある命令間で移動したりするのを防ぎます。ただし、関係のない操作に関しては移動する可能性があります。だから__volatile__
十分ではありません。
コンパイラのメモリが破壊されていることを通知します: "memory")
。"memory"
clobberは、GCCがasm全体でメモリの内容が同じままであるという仮定を立てることができないため、その周りで並べ替えられないことを意味します。
だから私の質問は:
- 1:私の理解
__volatile__
と"memory"
正しいですか? - 2:次の2つの呼び出しは同じことをしますか?
- 3:使用
"memory"
は、別のシリアル化命令を使用するよりもはるかに簡単に見えます。なぜ誰かが2番目のオプションよりも3番目のオプションを使用するのでしょうか?