【问题标题】:Is this compiler transformation allowed?允许这种编译器转换吗?
【发布时间】:2017-08-25 15:42:39
【问题描述】:

考虑这段代码,其中xy 是整数:

if (x)
    y = 42;

是否允许以下​​编译器转换?

int tmp = y;
y = 42;

if (!x)
    y = tmp;

上下文

这是来自 Bjarne Stroustrup 的常见问题解答:

// start with x==0 and y==0

if (x) y = 1;   // Thread 1 

if (y) x = 1;   // Thread 2

常见问题解答指出这是免费的数据竞争;如果xy 都为0,则不应写入任何变量。
但是如果允许转换呢?

【问题讨论】:

  • 是的(但它当然不能真正使用名称tmp)。但是你为什么在乎呢?
  • @BaummitAugen 真的吗?如果可以从其他线程访问 y,则转换可能会引入没有数据竞争的数据竞争。
  • @hvd 没有其他线程可以更改x 或访问y;那已经是UB了。明确地说:要么不会有新的比赛,要么之前的代码被破坏了。 (除非xstd::atomic<int>,但Q 没有这么说。)
  • @hvd 这就是为什么你必须提供同步。 as-of 规则假设只有一个线程
  • @BaummitAugen 假设 x0 并且从未修改过。假设两个线程运行 OP 的代码。那里没有 UB,即使没有同步。

标签: c++ multithreading c++11 data-race


【解决方案1】:

与我在错误评论中所写的不同,如果y 可能在线程之间共享并且编译器无法证明原始代码中存在任何现有UB,则实际上不允许这种转换。

标准明确规定:

将分配引入到潜在共享内存位置的编译器转换 本标准通常排除不会被抽象机器修改的内容,因为这样的 在抽象机器的情况下,分配可能会覆盖另一个线程的另一个分配 执行不会遇到数据竞争。

[intro.multithread] (1.10/22) 在 N3337 中,(1.10/25) 在 N4141 中。

因此,如果x 始终为 0,则原始代码将是无竞争的,而转换后的代码则不会。因此转换是不合法的。

【讨论】:

  • 快速提问,标准上说“通常被排除...”,这是否意味着它不会/不会被认为是非法的?还是由编译器决定?
  • @user3164339 这句话是 "Note:" 的一部分,它不像其他的那样正式,它更像是一个总结或重要的结论。 “一般排除”在此上下文中是指“除非特殊情况允许”
  • 特别是,与某些其他不正确的注释相反,as-if 规则本身没有对单个线程的任何限制。通过使数据竞争 UB,该标准允许优化器假设数据竞争不会发生并相应地进行优化,这实际上使优化器能够在很大程度上将他们的分析限制在单个线程中。但这绝不是允许实现在没有数据竞争时引入数据竞争的地方。
  • @T.C. “as-if 规则本身对单个线程没有任何限制”你说的好像 as-if 规则在某些范围内是无限的,而在其他范围内是有限的。
【解决方案2】:

如果是这样,那么您将无法排除对可从全局变量或其他变量访问的任何对象的访问。编译器甚至可以在进行间接调用时试探性地调用从未调用过的函数,然后通过恢复原始值“取消”它们的效果。

沿着这条优化悲观路径,它可以提前除以零,然后如果除数为零,则“忽略”结果,即使这是一个陷阱并且程序已停止。

这显然是荒谬的,无论标准是否应该如此,都必须拒绝。

【讨论】:

  • 但实际上是被拒绝了,所以应该没问题
  • @LWimsey 是的。但常识总是胜过标准文本。当常识认为编译器是正确的时,编译器不会被更改以符合要求。
猜你喜欢
  • 2019-04-21
  • 2010-11-16
  • 2011-06-21
  • 2019-11-22
  • 2012-08-22
  • 2013-10-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多