假设我们试图使用TSC进行性能监控,并且我们希望防止指令重新排序.
以下是我们的 Select :
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:这是打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:这是对rdtsc
的调用,在clobber列表中有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
我对第三种 Select 的理解如下:
进行调用__volatile__
防止优化器移除ASM或在可能需要ASM的结果(或改变输入)的任何指令之间移动它.但是,对于不相关的操作,它仍然可以移动它.所以__volatile__
是不够的.
告诉编译器内存正在被阻塞:: "memory")
."memory"
clobber意味着GCC不能对asm中保持不变的内存内容做出任何假设,因此不会对其重新排序.
所以我的问题是:
- 1:我对
__volatile__
和"memory"
的理解正确吗? - 他说:第二个电话是不是做了同样的事情?
- 3:使用
"memory"
看起来比使用另一条序列化指令简单得多.为什么有人会使用第三种 Select 而不是第二种 Select ?