证券投资网站建设,在哪里可以自己建网站,wordpress4.5.3,wordpress管理密码忘记编译乱序
现代的高性能编译器在目标码优化上都具备对指令进行乱序优化的能力。编译器可以对访存的指令进行乱序#xff0c;减少逻辑上不必要的访存#xff0c;以及尽量提高Cache命中率和CPU的Load/Store单元的工作效率。 因此在打开编译器优化以后#xff0c;看到生成的汇编…编译乱序
现代的高性能编译器在目标码优化上都具备对指令进行乱序优化的能力。编译器可以对访存的指令进行乱序减少逻辑上不必要的访存以及尽量提高Cache命中率和CPU的Load/Store单元的工作效率。 因此在打开编译器优化以后看到生成的汇编码并没有严格按照代码的逻辑顺序这是正常的。
解决办法
解决编译乱序问题需要通过barrier编译屏障进行。我们可以在代码中设置barrier屏障这个屏障可以阻挡编译器的优化。对于编译器来说设置编译屏障可以保证屏障前的语句和屏障后的语句不乱“串门”。 如__asm____volatile__
volatile
C语言volatile关键字的作用较弱它更多的只是避免内存访问行为的合并对C编译器而言volatile是暗示除了当前的执行线索以外其他的执行线索也可能改变某内存所以它的含义是“易变的”。 如果线程A读取var这个内存中的变量两次而没有修改var编译器可能觉得读一次就行了第2次直接取第1次的结果。但是如果加了volatile关键字来形容var则就是告诉编译器线程B、线程C或者其他执行实体可能把var改掉了因此编译器就不会再把线程A代码的第2次内存读取优化掉了。 另外volatile也不具备保护临界资源的作用。 总之Linux内核明显不太喜欢volatile
执行乱序后面的执行到前面
执行乱序则是处理器运行时的行为。这是处理器的“乱序执行Out-of-Order Execution”策略。高级的CPU可以根据自己缓存的组织特性将访存指令重新排序执行。连续地址的访问可能会先执行因为这样缓存命中率高。有的还允许访存的非阻塞即如果前面一条访存指令因为缓存不命中造成长延时的存储访问时后面的访存指令可以先执行以便从缓存中取数。因此即使是从汇编上看顺序正确的指令其执行的顺序也是不可预知的。
SMP上的乱序执行
对于大多数体系结构而言尽管每个CPU都是乱序执行但是这一乱序对于单核的程序执行是不可见的因为单个CPU在碰到依赖点后面的指令依赖于前面指令的执行结果的时候会等待所以程序员可能感觉不到这个乱序过程。 但是这个依赖点等待的过程在SMP处理器里面对于其他核是不可见的。比如若在CPU0上执行
我们不能武断地认为CPU0上打印的x一定等于42因为CPU1上即便“f1”编译在“x42”后面执行时仍然可能先于“x42”完成所以这个时候CPU0上打印的x不一定就是42。
解决办法
处理器为了解决多核间一个核的内存行为对另外一个核可见的问题引入了一些内存屏障的指令。譬如ARM处理器的屏障指令包括
DMB数据内存屏障在DMB之后的显式内存访问执行前保证所有在DMB指令之前的内存访问完成DSB数据同步屏障等待所有在DSB指令之前的指令完成位于此指令前的所有显式内存访问均完成位于此指令前的所有缓存、跳转预测和TLB维护操作全部完成ISB指令同步屏障Flush流水线使得所有ISB之后执行的指令都是从缓存或内存中获得的。 Linux内核的自旋锁、互斥体等互斥逻辑需要用到上述指令在请求获得锁时调用屏障指令在解锁时也需要调用屏障指令。
用处
前面提到每个CPU都是乱序执行但是单个CPU在碰到依赖点的时候会等待所以执行乱序对单核不一定可见。但是当程序在访问外设的寄存器时这些寄存器的访问顺序在CPU的逻辑上构不成依赖关系但是从外设的逻辑角度来讲可能需要固定的寄存器读写顺序这个时候也需要使用CPU的内存屏障指令。内核文档Documentation/memory-barriers.txt和Documentation/io_ordering.txt对此进行了描述。 Linux内核的自旋锁、互斥体等互斥逻辑需要用到上述指令在请求获得锁时调用屏障指令在解锁时也需要调用屏障指令。
各种内存屏蔽指令
在Linux内核中定义了读写屏障mb、读屏障rmb、写屏障wmb、以及作用于寄存器读写的__iormb、__iowmb这样的屏障API。读写寄存器的readl_relaxed和readl、writel_relaxed和writelAPI的区别就体现在有无屏障方面。