g++ / gcc 是否支持 C++20 新 atomic_flag 功能?
Are C++20 new atomic_flag features supported in g++ / gcc?
根据 cppreference,c++20 对 atomic_flag
操作有丰富的(并且对我有用)支持。
然而,尚不清楚 gcc 是否支持这些功能,在 gnu's feature summary 上找不到它们。我目前使用的是版本 8,设置了 -c++=2a
。
这段代码不能用 GCC8 编译:
#include <atomic>
int main() {
std::atomic_flag myFlag = ATOMIC_FLAG_INIT;
myFlag.test();
}
error: ‘struct std::atomic_flag’ has no member named ‘test’
我不想通过安装较新版本的 g++ 来破坏我的构建环境,如果有人可以报告对版本 10 或更高版本 atomic_flag
的支持,我将不胜感激。
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();
}
On the Godbolt compiler explorer 使用 gcc 和 clang:GCC10.2 不支持新的 C++20 atomic_flag::test()
成员函数,GCC 每晚主干构建支持。 Clang 11.0 和 trunk 有,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
有了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
这很奇怪也很可怕;即使内存中的值可能没有被布尔化,也没有理由不合并到 RAX 和 thenmovzx eax,al
的低字节,而不是仅仅在第一名.
但是 and eax,1
比 GCC 的疯狂 test/setnz/movzx 要好得多,如果它认为它需要重新布尔化的话。 (它实际上不需要这样做;ABI 保证内存中的 bool 是实际的 0
或 1
字节,并且 atomic<bool>
使用与 bool
.)
因此,对于 clang,两种方式都有愚蠢的错误优化转换为 int
。由于某种原因使用 GCC atomic_flag
不会遇到这个问题,但我不建议仅仅因为这个原因使用它。希望 atomic<bool>
会得到修复,通常你不会将 bool 转换为 int。
atomic<bool>
或 atomic_flag
的正常使用,如对其进行分支,不应有任何这些遗漏的优化。 例如
int g0, g1;
int conditional_load(std::atomic<bool> &myFlag) {
return myFlag ? g0 : g1;
}
# 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
所以这很正常。 Clang 选择在地址之间select,然后加载一次。这将加载使用延迟置于关键路径上并需要更多指令;当两个 vars 相邻时更糟糕的选择,因此可能来自相同的缓存行。 (GCC 的选择总是涉及两个变量,如果一个变量可以在缓存中保持“冷”状态,可能会更糟)。
根据 cppreference,c++20 对 atomic_flag
操作有丰富的(并且对我有用)支持。
然而,尚不清楚 gcc 是否支持这些功能,在 gnu's feature summary 上找不到它们。我目前使用的是版本 8,设置了 -c++=2a
。
这段代码不能用 GCC8 编译:
#include <atomic>
int main() {
std::atomic_flag myFlag = ATOMIC_FLAG_INIT;
myFlag.test();
}
error: ‘struct std::atomic_flag’ has no member named ‘test’
我不想通过安装较新版本的 g++ 来破坏我的构建环境,如果有人可以报告对版本 10 或更高版本 atomic_flag
的支持,我将不胜感激。
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();
}
On the Godbolt compiler explorer 使用 gcc 和 clang:GCC10.2 不支持新的 C++20 atomic_flag::test()
成员函数,GCC 每晚主干构建支持。 Clang 11.0 和 trunk 有,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
有了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
这很奇怪也很可怕;即使内存中的值可能没有被布尔化,也没有理由不合并到 RAX 和 thenmovzx eax,al
的低字节,而不是仅仅在第一名.
但是 and eax,1
比 GCC 的疯狂 test/setnz/movzx 要好得多,如果它认为它需要重新布尔化的话。 (它实际上不需要这样做;ABI 保证内存中的 bool 是实际的 0
或 1
字节,并且 atomic<bool>
使用与 bool
.)
因此,对于 clang,两种方式都有愚蠢的错误优化转换为 int
。由于某种原因使用 GCC atomic_flag
不会遇到这个问题,但我不建议仅仅因为这个原因使用它。希望 atomic<bool>
会得到修复,通常你不会将 bool 转换为 int。
atomic<bool>
或 atomic_flag
的正常使用,如对其进行分支,不应有任何这些遗漏的优化。 例如
int g0, g1;
int conditional_load(std::atomic<bool> &myFlag) {
return myFlag ? g0 : g1;
}
# 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
所以这很正常。 Clang 选择在地址之间select,然后加载一次。这将加载使用延迟置于关键路径上并需要更多指令;当两个 vars 相邻时更糟糕的选择,因此可能来自相同的缓存行。 (GCC 的选择总是涉及两个变量,如果一个变量可以在缓存中保持“冷”状态,可能会更糟)。