在C ++中捕获lambdas的全局引用是否会抑制别名优化?

时间:2014-04-02 11:26:52

标签: c++ c++11 lambda compiler-optimization strict-aliasing

在竞争条件下调试某些代码时出现了一个问题:这是一个简化示例:

//! Schedules a callable to be executed asynchronously
template<class F> void schedule(F &&f);

int main(void)
{
  bool flag(false);

  // Ignore the fact this is thread unsafe :)
  schedule([&] { flag=true; });

  // Can the compiler assume under strict aliasing that this check
  // for flag being false can be eliminated?
  if(!flag)
  {
    // do something
  }
  return 0;
}

显然代码片段是线程不安全的 - bool标志需要是std :: atomic然后seq_cst内存排序会强制编译器始终检查if测试的值。这个问题与此无关 - 它是关于初始化一个捕获 - 所有引用lambda是否告诉编译器该标志可能是别名,因此不能在稍后优化时检查标志值?

我自己的个人猜测是,构建一个[&amp; flag] {...} lambda会建议标志的潜在混叠,而[&amp;] {...}会破坏所有自动初始化变量并带有潜在的混淆声音太极端的反优化,所以我猜不到。但是,如果引用捕获lambda不会对clobber进行任何别名,我也不会感到惊讶。

给你C ++语言专家!我提前感谢。

编辑:我知道缺乏线程安全性会被视为一个答案,但这不是我所要求的。让我进一步减少我的榜样:

int main(void)
{
  bool flag(false);

  // Note that this is not invoked, just constructed.
  auto foo=[&] { flag=true; };

  // Can the compiler assume under strict aliasing that this check
  // for flag being false can be eliminated?
  if(!flag)
  {
    // do something
  }
  return 0;
}

现在可以检查标志是否为假?

编辑:对于那些将来来到这里的人,我对下面答案的最好理解是“是的,可以省略检查”,即构建一个lambda,它通过引用获取一个局部变量编译器的优化器不会将其视为可能修改该变量,因此编译器的优化器可以合法地忽略该变量存储的后续重新加载。感谢大家的回答。

1 个答案:

答案 0 :(得分:5)

你不能忽视缺乏线程安全性。数据竞争会产生未定义的行为,并且此代码具有数据竞争,因此答案是&#34;编译器是否可以在严格别名下假设可以消除对该标志为false的检查?&#34;是&#34;编译器可以做任何想做的事。&#34;

如果你修复了这个并使用std::atomic<bool>使代码线程安全,问题就会消失:编译器不能丢弃检查,因为它必须符合原子变量的内存模型要求。

如果schedule调用没有执行与多线程相关的任何操作,则编译器必须保留抽象机器的语义。抽象机器会检查标志的值,但编译器可能能够执行静态分析,证明标志始终具有特定值(始终为false或始终为true)并跳过校验。在as-if规则下允许这样做:不可能编写一个能够可靠地区分两种可能性(优化与否)的C ++程序。

因此,对于第二个例子,答案是&#34;编译器可以做任何想做的事情,只要可观察的行为与执行检查的行为相同。&#34;