为什么clang优化了一个循环轮询另一个线程写的变量?



当我在学习c++时,我发现了一些奇怪的事情…
我认为下面的代码会产生大数的结果(至少不是1.1)。
结果是在这里输入图像描述

其他编译器按预期工作。
但是具有激进优化的clang编译器似乎忽略了while循环。
所以我的问题是,我的代码有什么问题?或者这是clang想要的?

我使用了apple clang编译器(v14.0.3)

#include <iostream>
#include <thread>

static bool should_terminate = false;
void infinite_loop() {
long double i = 1.1;
while(!should_terminate)
i *= i;
std::cout << i;
}
int main() {
std::thread(infinite_loop).detach();
std::cout << "main thread";
for (int i = 0 ; i < 5; i++) {
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << ".";
}
should_terminate = true;
}

编译器资源管理器的汇编结果(clang v16.0.0, - 03)
这似乎也跳过了while循环。

_Z13infinite_loopv:                     # @_Z13infinite_loopv
sub     rsp, 24
fld     qword ptr [rip + .LCPI0_0]
fstp    tbyte ptr [rsp]
mov     rdi, qword ptr [rip + _ZSt4cout@GOTPCREL]
call    _ZNSo9_M_insertIeEERSoT_@PLT
add     rsp, 24
ret

你的代码有未定义的行为:

should_terminate不是原子对象,因此在一个线程中写入它并在另一个线程中可能并发地访问它(即没有任何同步)是数据竞争,这总是未定义的行为。

实际上,这个UB规则允许编译器做你在这里看到的优化。

编译器可以假设should_terminate在循环中永远不会改变,因为它不可能从另一个线程写入,因为这将是一个数据竞争。因此,当到达循环时,它要么是false并保持false,因此循环永远不会终止,要么是true,在这种情况下,循环体根本不执行。

然后,因为不执行任何原子/IO/volatile/同步操作的无限循环也会有UB,编译器可以进一步推断,当循环到达时,should_terminate必须(总是)true。因此,循环体永远不能被执行,删除循环是一种允许的优化。

所以Clang在这里的行为是正确的,而你的期望是错误的。should_terminate必须是std::atomic<bool>(或std::atomic_flag),这样对它的写操作与其他访问不同步,就不是数据竞争了。

没有同步机制或原子类型should_terminate变量不会提供您所期望的。包含互斥锁(同步)下面的代码正在生成无限循环。

#include <iostream>
#include <thread>
#include<mutex>
using namespace std;
mutex mu;
static bool should_terminate = false;
void infinite_loop() {
long double i = 1.1;
lock_guard<mutex> lock(mu);
while (!should_terminate)
{
cout << "From Child thread" << endl;
i *= i;
}

std::cout << i;
}
int main() {
std::thread(infinite_loop).detach();
std::cout << "main thread";
for (int i = 0; i < 5; i++) {
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << ".";
}
lock_guard<mutex> lock(mu);
should_terminate = true;
}

相关内容

  • 没有找到相关文章

最新更新