san*_*orn 5 c++ x86 gcc inline-assembly rdtsc
假设我们有一些相同的汇编的重复,其中包含RDTSC诸如
volatile size_t tick1;
asm ( "rdtsc\n" // Returns the time in EDX:EAX.
"shl $32, %%rdx\n" // Shift the upper bits left.
"or %%rdx, %q0" // 'Or' in the lower bits.
: "=a" (tick1)
:
: "rdx");
this_thread::sleep_for(1s);
volatile size_t tick2;
asm ( "rdtsc\n" // clang's optimizer just thinks this asm yields
"shl $32, %%rdx\n" // the same bits as above, so it just loads
"or %%rdx, %q0" // the result to qword ptr [rsp + 8]
: "=a" (tick2) //
: // mov qword ptr [rsp + 8], rbx
: "rdx");
printf("tick2 - tick1 diff : %zu cycles\n", tick2 - tick1);
printf("CPU Clock Speed : %.2f GHz\n\n", (double) (tick2 - tick1) / 1'000'000'000.);
Run Code Online (Sandbox Code Playgroud)
tick2 - tick1 diff : 0 cycles
CPU Clock Speed : 0.00 GHz
tick1 : bd806adf8b2
this_thread::sleep_for(1s)
tick2 : bd806adf8b2
Run Code Online (Sandbox Code Playgroud)
tick2 - tick1 diff : 2900160778 cycles
CPU Clock Speed : 2.90 GHz
tick1 : 14ab6ab3391c
this_thread::sleep_for(1s)
tick2 : 14ac17902a26
Run Code Online (Sandbox Code Playgroud)
tick2 - tick1 diff : 2900226898 cycles
CPU Clock Speed : 2.90 GHz
tick1 : 20e40010d8a8
this_thread::sleep_for(1s)
tick2 : 20e4aceecbfa
Run Code Online (Sandbox Code Playgroud)
[居住]
但是,让我们在后面添加tick3确切的内容asmtick2
volatile size_t tick1;
asm ( "rdtsc\n" // Returns the time in EDX:EAX.
"shl $32, %%rdx\n" // Shift the upper bits left.
"or %%rdx, %q0" // 'Or' in the lower bits.
: "=a" (tick1)
:
: "rdx");
this_thread::sleep_for(1s);
volatile size_t tick2;
asm ( "rdtsc\n" // clang's optimizer just thinks this asm yields
"shl $32, %%rdx\n" // the same bits as above, so it just loads
"or %%rdx, %q0" // the result to qword ptr [rsp + 8]
: "=a" (tick2) //
: // mov qword ptr [rsp + 8], rbx
: "rdx");
volatile size_t tick3;
asm ( "rdtsc\n"
"shl $32, %%rdx\n"
"or %%rdx, %q0"
: "=a" (tick3)
:
: "rdx");
Run Code Online (Sandbox Code Playgroud)
事实证明,GCC 认为tick3必须asm产生与 相同的值,tick2因为“显然”没有外部副作用,因此它只是从 重新加载tick2。即使这是错误的,但它有一个很强的观点。
tick2 - tick1 diff : 2900209182 cycles
CPU Clock Speed : 2.90 GHz
tick1 : 5670bd15088e
this_thread::sleep_for(1s)
tick2 : 567169f2b6ac
tick3 : 567169f2b6ac
Run Code Online (Sandbox Code Playgroud)
[居住]
在 C 模式下,GCC 和 Clang 的优化器都会受到影响。
换句话说,即使两者都优化了包含以下内容的块-O1的重复:asmrdtsc
tick2 - tick1 diff : 0 cycles
CPU Clock Speed : 0.00 GHz
tick1 : 324ab8f5dd2a
thrd_sleep(&(struct timespec){.tv_sec=1}, nullptr)
tick2 : 324ab8f5dd2a
tick3_rdx : 324b65d3368c
Run Code Online (Sandbox Code Playgroud)
[居住]
事实证明,所有优化器都可以对相同的非语句执行公共子表达式消除volatile asm,因此 asm 语句RDTSC需要为volatile。
C++ 标准未涵盖内联汇编,因此我不确定您在这里对“合法”的定义是什么。不过,您所看到的行为对我来说是有意义的,因为您正在运行内联程序集的副作用(即您的程序集没有实现纯函数)并且您忘记使用关键字volatile。来自GCC 内联汇编文档:
扩展 asm 语句的典型用途是操作输入值以产生输出值。然而,您的 asm 语句也可能会产生副作用。如果是这样,您可能需要使用 volatile 限定符来禁用某些优化。
还:
如果 GCC 的优化器确定不需要输出变量,有时会丢弃 asm 语句。此外,如果优化器认为代码将始终返回相同的结果(即,其输入值在调用之间不会发生变化),则优化器可能会将代码移出循环。使用 volatile 限定符会禁用这些优化。
如果您在问题消失volatile后立即插入关键字。asm
PS 不要使用内联汇编,只需包含x86intrin.h然后使用__rdtsc()函数即可。