zoukankan      html  css  js  c++  java
  • 《为什么在多核处理器下需要内存屏障(MenmoryBarrier)?》

    因为CPU的乱序执行技术虽然可以极大的提高流水线的工作效率,但是导致了实际运行次序和program的次序不一致,如果不做多余的防护措施,在逻辑次序上最后写入内存的数据未必最后写入。 也就是说,如果你期望最后写入一个标记数据表示前面的数据都已经准备好,然后在另外一个核心上依靠判断这个标记来判定一些数据就绪,这个策略并不可靠;

    比如以下代码:

    char* g_Data = nullptr;
    
    //processor 1 code:
    char* pTemp = new char[1000];
    g_Data = pTemp;
    
    //processor 2 code:
    if(g_Data != nullptr)
    {
        //do something...
    }

    这样的代码,在核心2上进行判断是不准确的,有可能核心1上的g_Data的值已经不是nullptr了,但是真正的1000个char还没有new出来,也就是说核心1上的g_Data保存着pTemp[0]的地址(标记位先被存到g_Data里面了),但是后面999个char还没真正开辟出来,这样,在核心2上进行访问就会造成程序crash。

    也就是说,在一个核心写入内存的数据未必真的最后写入,核与核之间作为一个整体来看的话,不能保证self-consitent。

    解决的办法是在标记为被写入前,强迫CPU串行化(也就是在上面demo中g_Data被赋值前强行CPU串行化):

    char* g_Data = nullptr;
    
    //processor 1 code:
    char* pTemp = new char[1000];
    MemoryBarrier();//add barrier, different platform has different API
    g_Data = pTemp;
    
    //processor 2 code:
    if(g_Data != nullptr)
    {
        //do something...
    }

    这样,在核心1上的g_Data被赋值前pTemp的内存就会是完整开辟出来的,就不会对核心2上的代码在访问g_Data的时候产生crash隐患。

    另:当CPU要访问的内存存在于cache的时候,像Interxxx(如InterLockedIncrement等)这样的原子操作命令是不会被发送到总线上的,取而代之会锁住cache。加锁,其实也是会锁住cpu的cache的,如果此时该核心又被让出去其他线程使用,那么这些原有的cache会被清掉,所以大量的锁也有这个副作用。

    PS:关于CPU的推测执行技术和乱序执行技术:

    推测执行技术:处理器为了提高性能,会去提前猜测接下去需要执行什么动作,然后提前执行,如果发现推测错误,则回滚至正常状态。通常应用的推测执行技术都能达到80%-90%的推测准确率。需要指出的是,推测执行技术是一个大类技术的统称,包括分支预测,预读取,推测性内存访问,缓存缺失的重叠/乱序处理(MESR)等等;

    乱序执行技术:当处理器遇到需要发生停顿的时间时(例如需要装在的数据发生了混存确实,需要去高延迟的内存中查找),处理器可以越过这个停顿事件,继续超前执行指令。同样,如果超前执行中发生了错误,也需要回滚至正常状态。

    乱序执行的微结构框图:

  • 相关阅读:
    JSP中 == 和equals的区别
    使用Cookie保存用户名密码,再次登陆时将Cookie用户名密码取出来并直接放置到用户名密码文本框中
    学习Java Web开发中遇到的问题,及其解决方法
    部署、测试、服务工作的经验记录
    Python基础--dict字典操作
    Python基础--dict字典
    Python基础--预留空 5
    Python基础--预留空 4
    Python基础--tuple 元组
    Python基础--预留3
  • 原文地址:https://www.cnblogs.com/DeanWang/p/7087959.html
Copyright © 2011-2022 走看看