首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >rdtscp,rdtsc之间的区别:内存和cpuid / rdtsc?

rdtscp,rdtsc之间的区别:内存和cpuid / rdtsc?
EN

Stack Overflow用户
提问于 2012-09-28 00:07:46
回答 2查看 34.1K关注 0票数 68

假设我们试图使用tsc进行性能监视,并且我们希望防止指令重新排序。

以下是我们的选择:

1: rdtscp是一个序列化调用。它防止围绕对rdtscp的调用重新排序。

代码语言:javascript
运行
复制
__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是一个序列化调用。

代码语言:javascript
运行
复制
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的调用,其中的memory位于clobber列表中,可以防止重新排序

代码语言:javascript
运行
复制
__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

我对第三种选择的理解如下:

通过调用__volatile__可以防止优化器移除asm或将其移动到任何可能需要asm的结果(或更改输入)的指令中。但是,对于不相关的行动,它仍然可以移动。所以__volatile__是不够的。

告诉编译器内存正在崩溃:: "memory")"memory"失败意味着GCC不能对整个asm中的内存内容保持不变做出任何假设,因此不会围绕它重新排序。

所以我的问题是:

  • 1:我对__volatile__"memory"的理解正确吗?
  • 他说:第二个电话做同样的事情吗?
  • 3:使用"memory"看起来比使用另一个序列化指令要简单得多。为什么有人会使用第三种选择而不是第二种选择?
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2012-09-28 06:41:54

正如注释中提到的,编译器屏障和处理器屏障之间有区别。asm语句中的volatilememory充当编译器的屏障,但是处理器仍然可以重新排序指令。

处理器屏障是必须显式给出的特殊指令,例如rdtscp, cpuid、内存隔离指令(mfence, lfence, .)等。

顺便说一句,虽然在使用cpuid作为rdtsc常见之前的一个障碍,但从性能的角度来看,它也可能非常糟糕,因为虚拟机平台通常会捕获和模拟cpuid指令,以便在集群中的多台计算机上强制使用一组通用的CPU功能(以确保实时迁移工作)。因此,最好使用内存隔离指令之一。

Linux内核在AMD平台上使用mfence;rdtsc,在Intel上使用lfence;rdtsc。如果您不想区分这两种情况,那么mfence;rdtsc在这两种情况下都能工作,尽管速度稍微慢一些,因为mfencelfence更强。

编辑2019-11-25:从LinuxVersion5.4开始,lfence用于在英特尔和AMD上序列化rdtsc。参见提交"x86: Remove X86_FEATURE_MFENCE_RDTSC":https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=be261ffce6f13229dad50f59c5e491f933d3167f

票数 49
EN

Stack Overflow用户

发布于 2013-01-08 11:44:10

您可以使用它,如下所示:

代码语言:javascript
运行
复制
asm volatile (
"CPUID\n\t"/*serialize*/
"RDTSC\n\t"/*read the clock*/
"mov %%edx, %0\n\t"
"mov %%eax, %1\n\t": "=r" (cycles_high), "=r"
(cycles_low):: "%rax", "%rbx", "%rcx", "%rdx");
/*
Call the function to benchmark
*/
asm volatile (
"RDTSCP\n\t"/*read the clock*/
"mov %%edx, %0\n\t"
"mov %%eax, %1\n\t"
"CPUID\n\t": "=r" (cycles_high1), "=r"
(cycles_low1):: "%rax", "%rbx", "%rcx", "%rdx");

在上面的代码中,第一个CPUID调用实现了一个屏障,以避免RDTSC指令上面和下面的指令的无序执行。使用这种方法,我们避免在读取实时寄存器之间调用CPUID指令。

然后,第一RDTSC读取时间戳寄存器,并将该值存储在内存中。然后执行我们要测量的代码。RDTSCP指令第二次读取时间戳寄存器,并保证我们要测量的所有代码的执行都完成了。随后传入的两个“mov”指令将edx和eax注册值存储到内存中。最后,CPUID调用确保再次实现一个屏障,因此在CPUID本身之前执行任何后续指令是不可能的。

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/12631856

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档