A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

编译乱序

编译器可以对访存的指令进行乱序,减少逻辑上不必要的访存,以及尽量提高 Cache 命中率和 CPU 的 Load/Store 单元的工作效率。因此在打开编译器优化以后,有时会看到生成的汇编码并没有严格按照代码的逻辑顺序。

解决编译乱序问题,需要通过 barrier() 编译屏障进行。可以在代码中设置 barrier() 屏障,这个屏障可以阻挡编译器的优化。

对于编译器来说,设置编译屏障可以保证屏障前的语句和屏障后的语句不乱“串门”。

在这里,再多说一句:关于解决编译乱序的问题,C 语言 volatile 关键字的作用较弱,它更多的只是避免内存访问合并的行为,对 C 编译器而言,volatile 是暗示除了当前的执行线索以外,其他的执行线索也可能改变内存,所以它的含义是“易变的”。


执行乱序

执行乱序是指即便编译器的二进制指令是顺序排放的,但是,在处理器执行时,后发射的执行还是可能先执行完,这里处理器的 “执行乱序(Out-of-Order Execution)”策略。例如高级的 CPU 可以根据自己缓存的组织特性,将访存的指令重新排序执行,连续地址的访问可能会先执行,因为这样命中率高,有的还允许访存的非阻塞,即如果前面的一条访存指令因为缓存不命中,造成长延时的存储访问时,后面的访存指令可以先执行,以便从缓存中取数。

为了解决这一问题,引入了内存屏障指令。


Linux 内核屏障指令

在 Linux 内核中定义了读写屏障 mb()、读屏障 rmb()、写屏障 wmb()、以及作用于寄存器读写的 __iormb()、__iowmb()这样的屏障 API。

在 Linux 的 I/O 内存读写中,没有 _relaxed 后缀的读写寄存器函数中含有屏障 API。

#define readb_relaxed(c) ({ u8  __v = ioswabb(__raw_readb(c)); __v; })       /* 读8bit */

...

#define readb(a) ({ u8  r_ = readb_relaxed(a); rmb(); r_; })/* 读8bit */

...


使用屏障指令访问外设

当程序访问外设的寄存器时,这些寄存器的访问顺序在 CPU 的逻辑上够不成依赖关系,但是从外设的逻辑角度来讲,可能需要固定的寄存器读写顺序,这个时候,也需要使用 CPU 的内存屏障指令。


1 个回复

倒序浏览

很不错,受教了
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马