B.Stroustrup新书中的优化和多线程

请参阅下面转录的B.Stroustrup的第41.2.2节“TCPL”第4版的指令重新排序

为了获得性能,编译器,优化器和硬件重新排序指令。 考虑:

// thread 1:
int x;
bool x_init;
void init()
{
    x = initialize(); // no use of x_init in initialize()
    x_init = true;
    // ...
}

对于这段代码,在分配给x_init之前没有指定给x的原因。 优化器(或硬件指令调度器)可能会首先执行x_init = true来决定加速该程序。 我们可能希望x_init指出x是否已经被initializer()初始化了。 但是,我们没有这么说,所以硬件,编译器和优化器都不知道。

将另一个线程添加到该程序中:

// thread 2:
extern int x;
extern bool x_init;
void f2()
{
    int y;
    while (!x_init) // if necessary, wait for initialization to complete
    this_thread::sleep_for(milliseconds{10});
    y = x;
    // ...
}

现在我们遇到了一个问题:线程2可能永远不会等待,因此会将未初始化的x分配给y。 即使线程1没有按照错误顺序设置x_init和x,我们仍然可能会遇到问题。 在线程2中,没有赋值给x_init,所以优化器可能会决定将!x_init的评估从循环中提取出来,以便线程2不会永远睡眠或永远休眠。

  • 标准是否允许在线程1中重新排序? (标准的一些引用即将出版)为什么这会加速该计划?
  • 在这个关于SO的讨论中的两个答案都似乎表明,当代码中存在全局变量时,不会出现这种优化,如上面的x_init。
  • 作者所说的“将!x_init的评估提升出循环”是什么意思? 这是这样的吗?

    if( !x_init ) while(true) this_thread::sleep_for(milliseconds{10});
    
    y = x;
    

  • 这不是C ++编译器/标准的问题,而是现代CPU的问题。 看看这里。 除非你告诉它,否则编译器不会在x和x_init的赋值之间发出内存屏障指令。

    在C ++ 11之前,它的价值是什么,该标准在抽象机器模型中没有多线程的概念。 现在事情有点好转。


  • C ++ 11标准不会“允许”或“阻止”重新排序。 它指定某种方式来强制一个特定的“障碍”,它会阻止编译器在它们之前/之后移动指令。 在这个例子中,编译器可能会对赋值进行重新排序,因为即使使用单个内核,它也可能对具有多个计算单元(ALU /超线程等等)的CPU更高效。 通常情况下,如果你的CPU有2个并行工作的ALU,那么编译器就不会试图为它们提供尽可能多的工作。 我并没有谈到在CPU内部完成的CPU指令的无序重新排序(例如),但编译时间排序是为了确保所有计算资源都忙于做一些工作。

  • 我认为这取决于编译器编译标志。 通常情况下,除非你告诉它,编译器必须假定另一个编译单元(比如B.cpp,在编译时不可见)可以有一个“extern bool x_init”,并且可以随时更改它。 然后,重新排序优化将与预期的行为相冲突(B可以定义initialize()函数)。 这个例子很简单,不太可能中断。 链接的SO答案与此“优化”无关,但只是在这种情况下,编译器无法假定全局数组未在外部进行修改,因此无法进行优化。 这不像你的例子。

  • 是。 这是一个非常常见的优化技巧,而不是:

  • //测试是一个布尔

    for (int i = 0; i < 345; i++) {
       if (test) do_something();
    }
    

    编译器可能会这样做:

    if (test) for(int i = 0; i < 345; i++) { do_something(); }
    

    并保存344无用的测试。

    链接地址: http://www.djcxy.com/p/77497.html

    上一篇: Optimization and multithreading in B.Stroustrup's new book

    下一篇: Longer screen timeout for a specific Android activity?