其他分享
首页 > 其他分享> > 指令重排和优化屏障

指令重排和优化屏障

作者:互联网

1. 优化带来的烦恼

用过GCC编译的同学应该知道GCC有O0、O1、O2、O3等优化选项,启用这些选项往往可以提高程序的运行效率,但它并不是万无一失的,尤其是在多线程场景下。而这些优化背后的技术正是指令重排。因为编译器或处理器也很难确定代码逻辑的原本意图。

锁能够保持原子性,但是经过编译器优化之后的代码,并不是绝对时序正确的,况且处理器还有可能进一步优化。这里面最经典的一个例子就是单例模式,Double-Checked Locking is Fixed In C++11

2. 内核提供的解决方案

内核提供以下方法,阻止编译器和处理器进行指令重排

屏障肯定是会影响性能的,但总不能为了优化性能而让程序出现错误。在任何程序面前,正确性永远是第一位的。

说明两个概念

内存屏障:rmb(), wmb(), mb(),可以防止硬件上的指令重排,针对的是处理器CPU。

优化屏障:barrier(),避免编译器对内存访问的优化,针对的是编译器。

它们在很多地方称为内存栅栏,但英文的话都是memory barrier。

3. 典型的应用

3.1 安全的单例模式

Double-Checked Locking 咋一眼看山去没什么问题,但是考虑下指令重排,就会发现问题。如果返回指针在new之前,那是我们不愿看到的,而这里使用屏障可以解决这个问题。

Singleton* Singleton::getInstance() {
    Singleton* tmp = m_instance;
    ...// insert memory barrier
    if (tmp == NULL) {
        Lock lock;
        tmp = m_instance;
        if (tmp == NULL) {
            tmp = new Singleton;
            ...// insert memory barrier
            m_instance = tmp;
        }
    }
    return tmp;
}

3.2 内核抢占

preempt_disable();
do_something();
preempt_enable();

在内核抢占中,preempt_disable对计数器加1,也就是告诉其他线程,不要来抢这个是我的, preempt_enable反之。

#define preempt_disable() \
do { \
    inc_preempt_count(); \
    barrier();  \
} while(0)

 

#define preempt_enable() \
do { \
     barrier();  \
    preempt_check_resched(); \
} while(0)

 

 

参考:

[0] 深入Linux内核架构

[1] https://preshing.com/20130930/double-checked-locking-is-fixed-in-cpp11/

 

标签:tmp,barrier,编译器,preempt,屏障,指令,重排,优化
来源: https://blog.csdn.net/niu91/article/details/116308436