我从
中查看了一些C代码http://www.mcs.anl.gov/~kazutomo/rdtsc.html
他们使用" 内联"," 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做了什么?
答案 0 :(得分:54)
__volatile__
块上的__asm__
修饰符强制编译器的优化器按原样执行代码。没有它,优化器可能会认为它可以直接删除,也可以从循环中取出并缓存。
这对rdtsc
指令很有用,如下所示:
__asm__ __volatile__("rdtsc": "=a" (a), "=d" (d) )
这不依赖,因此编译器可能会认为该值可以被缓存。易失性用于强制它读取新的时间戳。
单独使用时,如下:
__asm__ __volatile__ ("")
它实际上不会执行任何操作。但是,您可以对此进行扩展,以获得编译时内存屏障,该屏障不允许重新排序任何内存访问指令:
__asm__ __volatile__ ("":::"memory")
rdtsc
指令是volatile的一个很好的例子。当您需要计算一些指令执行的时间时,通常会使用rdtsc
。想象一下像这样的代码,你想要在r1
和r2
执行时间:
__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端屏障,以及&#34;内存&#34; volatile块中的说明符,它强制执行编译时屏障。在现代CPU上,您可以将mfence:rdtsc
替换为rdtscp
以获得更高效的效果。
答案 1 :(得分:4)
asm
用于将本机汇编代码包含在C源代码中。 E.g。
int a = 2;
asm("mov a, 3");
printf("%i", a); // will print 3
编译器有不同的变体。 __asm__
应该是同义词,可能有一些特定于编译器的差异。
volatile
表示可以从外部修改变量(也就是说不是由C程序修改)。例如,当编程微控制器时,存储器地址0x0000x1234
被映射到某些特定于设备的接口(即,当编码GameBoy时,按钮/屏幕/等以这种方式访问。)
volatile std::uint8_t* const button1 = 0x00001111;
这种禁用的编译器优化依赖于*button1
,除非被代码更改,否则不会更改。
它还用于多线程编程(今天不再需要?),其中一个变量可能被另一个线程修改。
inline
提示编译器“内联”对函数的调用。
inline int f(int a) {
return a + 1
}
int a;
int b = f(a);
不应将此编译为f
的函数调用,而应编译为int b = a + 1
。好像f
所在的宏。编译器主要根据功能使用/内容自动执行此优化。此示例中的__inline__
可能具有更具体的含义。
类似__attribute__((noinline))
(GCC特定语法)阻止函数内联。
答案 2 :(得分:-1)
__asm__
属性指定要在函数或变量的汇编代码中使用的名称。
__volatile__
限定符,通常用于嵌入式系统的实时计算,解决了status register
或ERROR
位READY
的编译器测试问题在优化期间导致问题。引入__volatile__
是为了告诉编译器该对象受到快速变化的影响,并强制该对象的每个引用都是真正的引用。