zoukankan      html  css  js  c++  java
  • 我对指令乱序和内存屏障的理解

    指令乱序有编译期间指令乱序和执行期间指令乱序,编译期间指令乱序可以通过我的这篇博文了解一下http://itblogs.ga/blog/20150329150706/,这篇文章里面简单谈下我对执行期间指令乱序的理解。

    这里是我自己的搭建的博客地址,欢迎访问:http://itblogs.ga/blog/20150421092401/

    声明

    文中一些观点是自己琢磨出来的,不保证正确,欢迎拍砖,纠正我的错误。如果深入了解这块的知识,可以阅读文中提供的一些资料,比较权威。

    先介绍关于指令的三个order

    摘自《Memory Ordering in Modern Microprocessors, Part I》

    Program order

    the order in which the memory operations are specified in the code running on a given CPU.

    Execution order

    the order in which the individual memory-reference instructions are executed on a given CPU. The execution order can differ from program order due to both compiler and CPU-implementation optimizations.

    本身执行就乱序了,相对于program order,这是由于1、编译器优化后生成的二进制指令顺序和程序写的不一致;2、CPU本身优化导致乱序执行。

    Perceived order:

    the order in which a given CPU perceives its and other CPUs' memory operations. The perceived order can differ from the execution order due to caching, interconnect and memory-system optimizations. Different CPUs might well perceive the same memory operations as occurring in different orders.

    Perceived order是在smp系统中才有的,举个例子,两个线程分别在两个core上运行:

    A=0 B=0 
    Pthread1: store A=1 store B=1 
    Pthread2: load  A=0 load  B=1

    明明pthread1先写的A,后写的B,按理说pthread2读到B=1了,A应该也是一才对。这是因为pthread2先看到了B的变化,还没看到A变化,对于pthread2来说,他看到的pthread1的执行顺序是store B=1 store A=1。也就是说pthread1本身执行的顺序是正确的,但pthread2看到的pthrad1执行的顺序是乱的。

    为什么会有execution order

    在单核时代,为了充分利用CPU资源,computer architects have used instruction-level parallelization techniques to improve processor perfor mance. Instruction-level parallelism (ILP), also known as dynamic, or out-of-order execution, gives the CPU the ability to reorder instructions in an optimal way to eliminate pipeline stalls. The goal of ILP is to increase the number of instructions that are executed by the processor on a single clock cycle. 摘自 《Multi-core programming Increasing Performance through Software Multi-threading》.

     单核时代Out of order带来的问题

    在访问设备时,通常会有这样的需求,先向端口A写入个数据,设备将结果写入到端口B,程序从端口B读取数据。

    write porta; read portb;

    这样的程序是严重依赖于顺序的,如果顺序反了,就读不到正确的值,问题就大了。

    怎么解决

    解决这个问题的方法就是引入了memory barrier,CPU提供memory barrier指令,保证了memory barrier前后的指令的执行顺序。

    write porta; memory barrier; read portb;

     这就是为什么我们在阅读驱动源码时,为什么遇到类似mb(); rmb();等的语句,这都是linux内核提供的memory barrier,封装了底层CPU指令。 关于memory barrier,《linux kernel source -- Documentation/memory-barriers.txt》是个很好的文档。

    我的疑问

    《linux kernel source -- Documentation/memory-barriers.txt》中有这样一段描述:

    Memory barriers are only required where there's a possibility of interaction between two CPUs or between a CPU and a device.  If it can be guaranteed that there won't be any such interaction in any particular piece of code, then memory barriers are unnecessary in that piece of code.

    单cpu 多线程不考虑吗?

    A = 0  B = 0   C[10] = {};
    ===================================
    thread1          |   thread2
    
                     |   if (A==1) {
    B = C;           |       access B 
    A = 1;           |   }

    如上代码,如果thread1的执行顺序乱了,对于thread2,后果是很严重的。

    对于这个问题,始终没有找到答案,我也只能尝试着回答一下。在单核时代,内存模型比较简单,遵循了sequential consistency《Shared Memory Consistency Models:A Tutorial》,即对内存操作会按顺序执行。而读写设备端口时,虽然发出了写端口的操作,但写操作没有完成,程序执行了读端口的命令,导致读的已经完成,而写还没完成,而出现乱序。内存系统如果有这样的情况发生,他能做到等写完再读,而对于独立的设备,他也保证这个的话,显然会比较复杂。

    为什么会有Perceived order

    到了多核时代,首先不只一个CPU执行指令了,然后,内存系统也复杂的多了,有一级、二级、三级缓存,并且每个核都有自己的缓存。

    不同的读指令,执行的时间就会不同,有直接拿自己的缓存的,有在内存中读的。指令执行的时间不同,必然导致流水线上指令进一步乱序。

    然后是写指令,指令执行完了,但写的操作不一定执行完了,可能只写在自己缓存上了,需要会写到内存中,可能还需要同步到其他的CPU的缓存中。写同步的速度不一致,也就带来了我们之前提到的Perceived order 问题。

    当然,如果内存系统还保证sequential consistency,那么这些问题就不存在了,但是多核情况下保证sequential consistency远远要比单CPU要复杂,多核并行的优势就完全体现不出来了,基本上商业CPU都没有保证sequential consistency。

    针对这样的问题,业界提成了多个内存一致性模型,不同的CPU有不同的处理,感兴趣的可以搜索以下文章了解:

    《Shared Memory Consistency Models:A Tutorial》

    《Memory Ordering in Modern Microprocessors, Part I》

     怎么办

    我们怎么应对呢,依然是使用memory barrier,对照于单核时代的mb,rmb,wmb,smp系统中使用smp_mb等,解决smp系统中的指令乱序问题。

    简单总结

    大部分编程中不会涉及到memory barrier的问题,并且一些原子操作和所有同步锁操作中帮我们处理了指令乱序的问题。但如果我们在并行编程中使用效率更高的无锁编程,那么就必须了解这方面的知识,摸清CPU的特性,正确的使用memory barrier。最好的方式是像内核那样,使用宏定义定义所有的memory barrier。在所有需要使用的地方都使用,只是在一些平台上,内存模型会更强一些,不需要,那就定义为空语句。

     我总结的比较浅,毕竟我也只是个入门,把这几天的看的东西总结以下,没有涉及很深。

    如果你需要了解的更加深入,以下材料值得阅读:

        linux kernel source -- Documentation/memory-barriers.txt

        Shared Memory Consistency Models:A Tutorial

        Memory Ordering in Modern Microprocessors, Part I

        Memory Consistency Models for Shared-Memory Multiprocessors

        Memory Consistency and Event Ordering in Scalable Shared-Memory Multiprocessors

        Multi-core programming Increasing Performance through Software Multi-threading

      

    原文链接:http://itblogs.ga/blog/20150421092401/  转载请注明出处

       

  • 相关阅读:
    目标检测梳理:基于深度学习的目标检测技术演进:R-CNN、Fast R-CNN、Faster R-CNN(转)
    4. 基于深度学习的目标检测算法的综述(转)
    2. 滑动窗口和 Bounding Box 预测(转)
    1. 初步认识目标定位、特征点检测、目标检测(转)
    redux (一)
    react-todoMVC脚手架
    SVN提示图标详解
    react ( 二 )
    react入门(一)
    JavaScript奇技淫巧
  • 原文地址:https://www.cnblogs.com/jintianfree/p/4444113.html
Copyright © 2011-2022 走看看