【问题标题】:Why is the compiler allowed to optimize out this busy waiting loop?为什么允许编译器优化这个繁忙的等待循环?
【发布时间】:2021-10-28 05:57:32
【问题描述】:
#include <iostream>
#include <thread>
#include <mutex>

int main()
{
    std::atomic<bool> ready = false;

    std::thread threadB = std::thread([&]() {
        while (!ready) {}

        printf("Hello from B\n");
    });

    std::this_thread::sleep_for(std::chrono::seconds(1));

    printf("Hello from A\n");

    ready = true;

    threadB.join();

    printf("Hello again from A\n");
}

这是来自 CppCon 谈话 https://www.youtube.com/watch?v=F6Ipn7gCOsY&ab_channel=CppCon(分钟 17)的一个例子

目标是首先打印Hello from A,然后让threadB 启动。很明显,应该避免忙等待,因为它会占用大量 CPU。

作者说while (!ready) {} 循环可以由编译器优化(通过将ready 的值放入寄存器),因为编译器看到threadB 永远不会休眠,因此ready 永远不会被更改。但是即使线程从不休眠,另一个线程仍然可以改变值,对吧?没有数据竞争,因为ready 是原子的。作者声明这段代码是UB。有人能解释一下为什么允许编译器做这样的优化吗?

【问题讨论】:

  • 我认为扬声器不正确,并且无法优化此循环。 std::atomic 的全部意义在于它实际上可以自发改变,编译器不能假设其他情况。删除循环将极大地改变程序的可观察行为,因此不是有效的优化。
  • Sleep 告诉 CPU 在一定时间内不需要,仅此而已(与花费相同时间的逻辑没有区别),您的循环不会得到优化(已删除)除非它是空的。
  • @Top-Master 循环为空 - 它调用ready.load()
  • 我认为既然这里有两个选择(优化循环,不要优化循环),这将是一个问题,这是否是未指定的行为,而不是未定义的行为
  • 最好将原子视为防止切片和排序。它们也不是隐式易变的。

标签: c++ multithreading compiler-optimization busy-waiting


【解决方案1】:

作者在视频下方的comments 之一中承认他错了:

我是这么想的,但看来我错了;编译器无法将原子读取提升到循环之外。 @17:54 的建议仍然是正确的——你仍然应该非常小心并提防编译器可能会重新排序、合并或消除一般的原子访问的情况——但这个特殊的 while 循环实际上并不是这种情况。 有关编译器如何优化原子访问模式的一些(大部分是理论上的)示例,请参阅 JF Bastien 的 N4455 “No Sane Compiler would Optimize Atomics”http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4455.html

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-11-10
    • 2019-02-19
    • 2017-01-27
    • 2015-05-13
    • 2021-12-20
    • 1970-01-01
    • 1970-01-01
    • 2019-04-21
    相关资源
    最近更新 更多