任务调度与伪共享问题

来源:互联网 发布:王国风云2 mac 编辑:程序博客网 时间:2024/05/21 19:03

        由于伪共享问题的存在,再循环中,对数组等可能存放在统一cache行中的数据进行操作时,需要考虑伪共享问题的影响,需要避免两个不同的硬件线程操作数组中的相邻元素。

        采用不同的调度策略,可能会对伪共享问题有不同的影响,比如某些调度策略容易使线程同时操作数组中的相邻元素,而另外一些调度策略则使不同硬件线程操作数组中相隔比较远的元素,从而有效避免伪共享问题的出现。


       

        注释:伪共享


        CPU内部也会有自己的缓存,内部的缓存单位是行,叫做缓存行。在多核环境下会出现CPU之间的内存同步问题(比如一个核加载了一份缓存,另外一个核也要用到同一份数据),如果每个核每次需要时都往内存中存取,这会带来比较大的性能损耗,这个问题一般是通过MESI协议来解决的。

          MESI协议中包含M、E、S、I四个状态,分别的意思是:

  • M(修改, Modified): 本地处理器已经修改缓存行, 即是脏行, 它的内容与内存中的内容不一样. 并且此cache只有本地一个拷贝(专有).
  • E(专有, Exclusive): 缓存行内容和内存中的一样, 而且其它处理器都没有这行数据
  • S(共享, Shared): 缓存行内容和内存中的一样, 有可能其它处理器也存在此缓存行的拷贝
  • I(无效, Invalid): 缓存行失效, 不能使用
         
          cpu在对缓存行进行了不同的操作后,在cpu缓存行中会记录缓存的不同状态。当一个核要对共享的数据进行写操作时,需要给其他核发送RFO(REQUEST FOR OWNER)消息并把其他核的数据改成I态。这是一种比较消耗性能的操作。
          cpu的伪共享问题本质是:几个在逻辑上并不包含在同一个内存单元内的数据,由于被cpu加载在同一个缓存行当中,当在多线程环境下,被不同的cpu执行,导致缓存行失效而引起的大量的缓存命中率降低。
          例如:当两个线程分别对一个数组中的两份数据进行写操作,每个线程操作不同index上的数据,看上去,两份数据之间是不存在同步问题的,但是,由于他们可能在同一个cpu缓存行当中,这就会使这一份缓存行出现大量的缓存失效,如前所述当一份线程更新时要给另一份线程发送RFO消息并把它的缓存失效掉。
          解决这个问题的一个办法是让这个数组中不同index的数据在不同的缓存行:因为缓存行的大小是64个字节,那我们只要让数组中没份数据的大小大于64个字节,就可以保证他们在不同的缓存行当中,就能避免这样的伪共享问题。
          比如一个类当中原本只有一个long类型的属性。这样这个类型的对象只占了16个字节(java对象头有8字节),如果这个类型被定义成一个长度为4的数组,这个数组的所有数据都可能在一个缓存行当中,就可能出现伪共享问题,那么这个时候,就可以采用补齐(padding)的办法,在这个类型中加上public long a,b,c,d,e,f,g;这六个无用的属性定义,使得这个类型的一个实例占用内存达到64字节,这样这个类型的伪共享问题就得到了解决,在多线程当中对这个类型的数组进行写操作就能避免伪共享问题。      


摘自:周伟明,多核计算与程序设计        【1】   
           http://blog.csdn.net/e5945/article/details/8010779      【2】              



0 0
原创粉丝点击