- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我研究了一些来自
的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)
最佳答案
__asm__
block 上的 __volatile__
修饰符强制编译器的优化器按原样执行代码。如果没有它,优化器可能会认为它可以完全删除,或者从循环中提取并缓存。
这对 rdtsc
指令很有用,如下所示:
__asm__ __volatile__("rdtsc": "=a" (a), "=d" (d) )
这不需要任何依赖关系,因此编译器可能会假设该值可以被缓存。 Volatile 用于强制它读取新的时间戳。
单独使用时,像这样:
__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 block 。要解决此问题,您需要添加一些强制序列化的障碍:
__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 block 中的“内存”说明符强制执行编译时屏障。在现代 CPU 上,您可以将 mfence:rdtsc
替换为 rdtscp
以提高效率。
关于c - __asm__ __volatile__ 在 C 中做什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26456510/
我想把C的outb函数移植到D上。 static __inline void outb (unsigned char value, unsigned short int port) { __a
我有一个分配内存的代码,将一些缓冲区复制到分配的内存,然后跳转到该内存地址。 问题是我无法跳转到内存地址。我正在使用 gcc 和 __asm__ 但我无法调用该内存地址。 我想做这样的事情: addr
我在预处理的 C 代码中看到了以下代码。 asm 在函数声明后做什么? extern int scanf (const char *__restrict __format, ...) __asm__
在尝试编译我的项目时,它使用了一些第三方头文件,使用 mingw 4.4,我遇到了以下错误: Assembler messages: Error: junk at end of line, first
几天以来我一直在尝试编写一个非常简单的内联汇编代码,但没有任何效果。我有 NetBeans 作为 IDE 和 MinGW 作为编译器。我最新的代码是: uint16 readle_uint16(con
我想在 c 中使用 char 变量并将其添加到汇编代码中。 char c = 'X'; __asm__ __volatile__("movb '"c"', %ah\n"); 但是当我使用它时,它说需要
我的项目使用C++,包含一个c头文件ira.h如下: #ifdef __cplusplus extern "C" { #endif extern inline void disable_ir
这个问题在这里已经有了答案: CPUID implementations in C++ (3 个答案) 关闭去年。 我正在尝试使用以下 cod 读取 cpuid 信息,但它不起作用。我正在使用 Vi
__asm__ __volatile__ () 的基本作用是什么?对于 ARM 架构,“内存” 的意义是什么? 最佳答案 asm volatile("" ::: "memory"); 创建一个编译器级
我有一段代码调用了 BIOS 文本输出函数,INT 0x10, AH=0x13: static void __stdcall print_raw(uint8_t row, uint8_t col, u
这是一本旧书上的一个小函数: unsigned long f() { __asm__("movl %eax,%esp"); } 函数解释为获取堆栈点,但似乎不是。它实际上获取了一个比我的帧地址低
这个问题在这里已经有了答案: Calling printf in extended inline ASM (1 个回答) 关闭 3 年前。 我正在尝试使用 c __asm__ 打印 AAAA,如下所
我使用的是 VS2012 C++ Windows 7,我需要获取有关 CPU 多线程的信息以计算可用逻辑处理器的数量。 我正在使用此代码(来自 This SO Post) typedef __int3
据我所知,__asm { ... }; 之间的唯一区别和 __asm__("...");是第一次使用mov eax, var第二个使用 movl %0, %%eax与 :"=r" (var)在末尾。还
我研究了一些来自 的C代码 http://www.mcs.anl.gov/~kazutomo/rdtsc.html 他们使用诸如 __inline__、__asm__ 等东西,如下所示: 代码1: s
我试图理解这段代码中发生了什么,特别是在 __asm__ 中。我如何单步执行汇编代码,以便打印每个变量以及不打印什么? 具体来说,我试图逐步解决这个问题,以弄清楚 8() 是什么意思,并了解它如何知道
我在 Linux 环境中处理 Nasm 和 GNU C 内联 asm 有一段时间了,这个功能工作得很好......但现在我正在切换到 Windows 环境,我想使用 Masm(与 VS2008)我不能
这段代码我是在x86_64上写的,编译运行良好, __asm__("nop" : "=eax"(foo) : //"eax"(foo),"ebx
这个问题已经有答案了: What does double underscore ( __const) mean in C? (4 个回答) 已关闭 7 年前。 在c中使用(__)“双下划线”的含义是什
一个代码库有一个 COMPILER_BARRIER宏定义为 __asm__ volatile("" ::: "memory") .宏的目的是防止编译器跨屏障重新排序读写。请注意,这显然是编译器屏障,不
我是一名优秀的程序员,十分优秀!