C++ 为什么全局变量会给函数调用中的编译器优化带来麻烦?

C++ 为什么全局变量会给函数调用中的编译器优化带来麻烦?,c++,c,pthreads,C++,C,Pthreads,发件人: 防止编译器移动内存 围绕函数调用的操作,例如 pthread mutexlock(),它们基本上被视为 对不透明函数的调用,编译器 他没有任何消息。编译器有效地假设 pthread mutexlock()可以读取或写入任何 全局变量因此,内存引用不能简单地在调用中移动。这种方法还可以确保传递性调用,例如对函数的调用f() 然后调用pthread mutexlock(),将被处理 以同样的方式,或多或少适当地,即内存操作不会在调用f() 或者,无论是否正在运行整个用户程序 立即分析 为什

发件人:

防止编译器移动内存 围绕函数调用的操作,例如 pthread mutex
lock()
,它们基本上被视为 对不透明函数的调用,编译器 他没有任何消息。编译器有效地假设 pthread mutex
lock()
可以读取或写入任何 全局变量因此,内存引用不能简单地在调用中移动。这种方法还可以确保传递性调用,例如对函数的调用
f()
然后调用pthread mutex
lock()
,将被处理 以同样的方式,或多或少适当地,即内存操作不会在调用
f()
或者,无论是否正在运行整个用户程序 立即分析


为什么会这样呢?关于为什么不能移动引用,有什么反例吗?

编译器可以随意移动代码。(稍微简化的)要求是没有明显的副作用

本文描述了为什么需要在编译器级别而不是库级别支持线程。让我们考虑编译器优化代码时的意义。我将从Kerrek SB的优秀示例开始,因为此回复太长,无法发表评论

int x = 0;
pthread_mutex_lock(&m);
x = y;
优化器会看到一个值,该值不会被修改,但会被设置两次。copmiler可以访问函数内部的代码,并且可以看到任何东西都不可能修改赋值的值。由于没有明显的副作用,优化器可以消除对零的赋值,只需将其替换为y值。优化器将删除它,并将其转换为:

pthread_mutex_lock(&m);
int x = y;
这可能不会影响任何事情,变量x是局部变量,没有其他影响

现在,让我们做一个更有问题的人为例子

if(globals.hasData) {
  int prelock_value = globals.foo;
  pthread_mutex_lock(&m);
  if(prelock_value != globals.foo) {
    // value changed before we could lock it, do something different
    DoSpecialStuffSinceValueChangedWhileWaiting();
    pthread_mutex_unlock(&m);
    return;
  }
  DoOtherStuff();
  ...
现在我们将从优化器的角度来看这一点。优化器看到您读取了一个值,然后您做了一些不修改该值的事情,然后您根据刚才存储的值进行测试。由于它看不到明显的副作用,因此可能会删除如下比较:

if(globals.hasData) {
  int prelock_value = globals.foo;
  pthread_mutex_lock(&m);
  if( false /* always false: prelock_value != globals.foo */ ) {
    // value changed before we could lock it, do something different
    DoSpecialStuffSinceValueChangedWhileWaiting();
    pthread_mutex_unlock(&m);
    return;
  }
  DoOtherStuff();
  ...
然后,它再次尝试删除死代码。它看到一个不必要的整数赋值,一个不必要的条件,因为if的结果总是false,并得出以下结论:

if(globals.hasData) {
  pthread_mutex_lock(&m);
  // everything was removed.
  DoOtherStuff();
如果您将其与原始函数进行比较,很明显这根本不是程序员想要的

多年来发现了大量潜在的优化。他们中的许多人假设什么时候可以安全地将代码从一个地方移动到另一个地方,或者假设值仅由该代码块修改。在并发编程中,这些假设可能会严重破坏


优化器需要了解某些功能无法移动,某些功能充当无法跨越的障碍,或者使优化器的假设无效。

我不确定您是否正确理解了文本。它讨论的是在硬件级别访问内存(加载和存储)。“内存引用”类似于x86中的
mov[eax]0
。内存访问操作的顺序是并发程序执行的核心问题之一(另一个问题是其效果的传播)。是的,我理解。但在这一段中,我认为编译器不执行指令重新排序的原因是函数访问全局变量的可能性。我想知道为什么会这样,也就是说,如果编译器重新排序并且函数中访问了一个全局变量,那么可能会出现什么问题;pthread_mutex_lock(&m);x=y则不能仅将
y
的负载移过锁获取,因为在获取锁之前,无法保证其所需值已变为可见。