Vor*_*ord 1 c++ gcc g++ stdatomic
根据cppreference,c++20 具有丰富的(对我来说很有用)的atomic_flag
操作支持。
但是,尚不清楚 gcc 是否还支持这些功能,在gnu 的功能摘要中找不到它们。我目前正在使用带有-c++=2a
set 的版本 8 。
这段代码不能用 GCC8 编译:
#include <atomic>
int main() {
std::atomic_flag myFlag = ATOMIC_FLAG_INIT;
myFlag.test();
}
Run Code Online (Sandbox Code Playgroud)
error: ‘struct std::atomic_flag’ has no member named ‘test’
我不想通过安装较新版本的 g++ 来破坏我的构建环境,并且感谢任何可以报告atomic_flag
版本 10 或更高版本支持的人。
atomic<bool>
做任何事情atomic_flag
,就像在所有普通 C++ 实现上一样高效。C++20 刚刚向 atomic_flag 添加了新的东西,使其达到atomic<bool>
. atomic_flag
为保证是lock_free,但在所有平台上有人关心的做法,所以是atomic<bool>
。
不要期望 GCC8 具有所有 C++2a 特性;至少在https://godbolt.org/上尝试使用最新版本或每晚 gcc。(另请注意,需要支持这一点的不是编译器本身,只是标准库头文件。但 libstdc++ 通常与 g++ 一起分发。)
我调整了您的示例,以便可以在启用优化的情况下编译它,而无需优化实际工作。
#include <atomic>
int flagtest(std::atomic_flag &myFlag) {
//std::atomic_flag myFlag = ATOMIC_FLAG_INIT;
return myFlag.test();
}
Run Code Online (Sandbox Code Playgroud)
在带有 gcc 和 clang的 Godbolt 编译器资源管理器上:GCC10.2 不支持新的 C++20atomic_flag::test()
成员函数,GCC nightly trunk build 支持。Clang 11.0 和主干可以,clang 10.0.1 不行。
# GCC trunk for x86-64 -O3 -std=gnu++2a
flagtest(std::atomic_flag&):
movzx eax, BYTE PTR [rdi]
ret
booltest(std::atomic<bool>&):
movzx eax, BYTE PTR [rdi]
test al, al
setne al
movzx eax, al # this is weird, GCC has gone insane.
ret
Run Code Online (Sandbox Code Playgroud)
使用 clang,我们还可以尝试 libc++(C++ 标准库的新实现)。默认情况下,Linux(包括 Godbolt)上的 clang 使用 libstdc++,就像 GCC 一样。
# clang 11.0 -O3 -std=gnu++2a -stdlib=libc++
flagtest(std::__1::atomic_flag&): # @flagtest(std::__1::atomic_flag&)
mov al, byte ptr [rdi]
movzx eax, al
and eax, 1
ret
booltest(std::__1::atomic<bool>&): # @booltest(std::__1::atomic<bool>&)
mov al, byte ptr [rdi]
movzx eax, al
and eax, 1
ret
Run Code Online (Sandbox Code Playgroud)
这太奇怪了,太可怕了;即使内存中的值可能没有被布尔化,也没有理由不合并到 RAX 的低字节,然后 movzx eax,al
,而不是首先进行 movzx 加载。
但是and eax,1
,如果 GCC 认为需要重新布尔化,那么它比 GCC 的疯狂 test/setnz/movzx 更糟糕。(它实际上并不需要这样做;ABI 保证内存中的 bool 是实际的0
或1
字节,并atomic<bool>
使用与 相同的对象表示bool
。)
因此,使用 clang,两种方式都会将愚蠢的错过优化转换为int
. 由于某种原因atomic_flag
,使用 GCC不会遇到这个问题,但我不建议仅仅因为这个原因使用它。希望atomic<bool>
会得到修复,通常您不会将 bool 转换为 int。
正常使用atomic<bool>
or atomic_flag
,就像在它上面分支一样,不应该有任何这些遗漏的优化。 例如
int g0, g1;
int conditional_load(std::atomic<bool> &myFlag) {
return myFlag ? g0 : g1;
}
Run Code Online (Sandbox Code Playgroud)
# gcc 11 nightly build -O3
conditional_load(std::atomic<bool>&):
movzx eax, BYTE PTR [rdi]
test al, al
mov eax, DWORD PTR g0[rip]
cmove eax, DWORD PTR g1[rip]
ret
Run Code Online (Sandbox Code Playgroud)
所以这很正常。Clang 选择在地址之间进行选择,然后加载一次。这将负载使用延迟放在关键路径上并需要更多指令;当两个变量相邻时更糟糕的选择,所以可能来自同一个缓存行。(GCC 的选择总是涉及两个变量,如果可以在缓存中保持“冷”状态可能会更糟)。