我研究了一些C代码
http://www.mcs.anl.gov/~kazutomo/rdtsc.html
他们使用诸如__inline____asm__等之类的东西,如下所示:
代码1:

static __inline__ tick gettick (void) {
    unsigned a, d;
    __asm__ __volatile__("rdtsc": "=a" (a), "=d" (d) );
    return (((tick)a) | (((tick)d) << 32));
}
代码2:
volatile int  __attribute__((noinline)) foo2 (int a0, int a1) {
    __asm__ __volatile__ ("");
}
我想知道code1和code2是做什么的?
(编者注:对于此特定的RDTSC用例,首选内部函数:How to get the CPU cycle count in x86_64 from C++?另请参见https://gcc.gnu.org/wiki/DontUseInlineAsm)

最佳答案

__volatile__块上的__asm__修饰符强制编译器的优化器按原样执行代码。如果没有它,优化器可能会认为它可以直接删除,也可以退出循环并进行缓存。

这对于rdtsc指令很有用,如下所示:

__asm__ __volatile__("rdtsc": "=a" (a), "=d" (d) )

这没有依赖关系,因此编译器可能会假定可以缓存该值。 Volatile 用于强制其读取新的时间戳。

单独使用时,如下所示:
__asm__ __volatile__ ("")

它实际上不会执行任何操作。但是,您可以扩展它,以获取不允许重新排序任何内存访问指令的编译时内存障碍:
__asm__ __volatile__ ("":::"memory")
rdtsc指令是volatile的一个很好的例子。 rdtsc通常在需要时间执行某些指令的时间时使用。想象一下这样的代码,您想在其中计时r1r2的执行时间:
__asm__ ("rdtsc": "=a" (a0), "=d" (d0) )
r1 = x1 + y1;
__asm__ ("rdtsc": "=a" (a1), "=d" (d1) )
r2 = x2 + y2;
__asm__ ("rdtsc": "=a" (a2), "=d" (d2) )

在这里,实际上允许编译器缓存时间戳,有效的输出可能显示每行执行的时钟恰好为0。显然,这不是您想要的,因此您引入__volatile__来防止缓存:
__asm__ __volatile__("rdtsc": "=a" (a0), "=d" (d0))
r1 = x1 + y1;
__asm__ __volatile__("rdtsc": "=a" (a1), "=d" (d1))
r2 = x2 + y2;
__asm__ __volatile__("rdtsc": "=a" (a2), "=d" (d2))

现在,您每次都将获得一个新的时间戳,但是仍然存在一个问题,即允许编译器和CPU重新对所有这些语句进行排序。在已经计算出r1和r2之后,它可能最终执行asm块。要解决此问题,您将添加一些强制序列化的障碍:
__asm__ __volatile__("mfence;rdtsc": "=a" (a0), "=d" (d0) :: "memory")
r1 = x1 + y1;
__asm__ __volatile__("mfence;rdtsc": "=a" (a1), "=d" (d1) :: "memory")
r2 = x2 + y2;
__asm__ __volatile__("mfence;rdtsc": "=a" (a2), "=d" (d2) :: "memory")

请注意此处的mfence指令,该指令强制执行CPU端屏障,而volatile块中的“内存”说明符则强制执行编译时屏障。在现代CPU上,您可以将mfence:rdtsc替换为rdtscp,以提高效率。

关于c - __asm__ __volatile__在C中做什么?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/26456510/

10-10 22:15