为什么 g++ O2 选项使无符号环绕不起作用?
Why g++ O2 option make unsigned wrap around not working?
我试图用 C++ 编写一个队列,我从 intel dpdk libring 了解到我可以通过使用无符号环绕 属性:
编写类似的代码来做到这一点
#include <cstdio>
#include <cassert>
#include <atomic>
#include <thread>
size_t global_r = 0, global_w = 0, mask_ = 3;
void emplace() {
unsigned long local_w, local_r, free_entries = 0;
local_w = global_w;
while (free_entries == 0) {
local_r = global_r;
free_entries = (mask_ + local_r - local_w);
}
fprintf(stderr, "%lu\n", free_entries);
auto w_next = local_w + 1;
std::atomic_thread_fence(std::memory_order_release);
global_w = w_next;
}
void pop() {
unsigned long local_r = global_r;
unsigned long r_next = local_r + 1;
// make sure nobody can write to it before destruction
std::atomic_thread_fence(std::memory_order_release);
global_r = r_next;
}
int main() {
std::jthread([]() -> void {
int i = 10;
while (i-- >= 0) emplace();
});
std::jthread([]() -> void {
int i = 10;
while (i-- >= 0) pop();
});
return 0;
}
当我用 g++ O0 和 O2 运行 时,它会产生不同的结果:
使用 O2:
3
2
1
0
18446744073709551615
18446744073709551614
18446744073709551613
18446744073709551612
18446744073709551611
18446744073709551610
18446744073709551609
无 O2:
3
2
1
.....long time suspending
请问我对unsinged wrap around的理解有没有错? (我从几个 Whosebug post 和其他参考资料中了解到未标记的环绕是定义的行为)。
您是否知道一旦 global_w
递增到 3 然后 emplace()
中的 while
循环变成无限循环? AFAIK,无限循环导致 C++ 中的未定义行为。
我认为您的问题是因为您将 std::jthread
对象定义为临时对象。这意味着它们在它们出现的表达式末尾被破坏。因此,两个线程不会 运行 并行(同时)。
您可以通过创建线程变量轻松更改它,这些变量将在 main()
:
结束时销毁
int main()
{
std::thread t1 ([]() -> void { // note that "t1" variable name
int i = 10;
while (i-- >= 0) emplace();
});
std::thread t2 ([]() -> void { // note that "t2" variable name
int i = 10;
while (i-- >= 0) pop();
});
}
然而,即便如此,我认为您在 global_r
上存在 数据竞争 ,这会导致 未定义的行为 作为出色地。如果没有它的同步写入,编译器可能很容易假设 emplace()
具有对 global_r
的独占访问权,并有效地从循环中“删除”此读取 local_r = global_r;
。
此类问题的现场演示:https://godbolt.org/z/566WP9n36。
我试图用 C++ 编写一个队列,我从 intel dpdk libring 了解到我可以通过使用无符号环绕 属性:
编写类似的代码来做到这一点#include <cstdio>
#include <cassert>
#include <atomic>
#include <thread>
size_t global_r = 0, global_w = 0, mask_ = 3;
void emplace() {
unsigned long local_w, local_r, free_entries = 0;
local_w = global_w;
while (free_entries == 0) {
local_r = global_r;
free_entries = (mask_ + local_r - local_w);
}
fprintf(stderr, "%lu\n", free_entries);
auto w_next = local_w + 1;
std::atomic_thread_fence(std::memory_order_release);
global_w = w_next;
}
void pop() {
unsigned long local_r = global_r;
unsigned long r_next = local_r + 1;
// make sure nobody can write to it before destruction
std::atomic_thread_fence(std::memory_order_release);
global_r = r_next;
}
int main() {
std::jthread([]() -> void {
int i = 10;
while (i-- >= 0) emplace();
});
std::jthread([]() -> void {
int i = 10;
while (i-- >= 0) pop();
});
return 0;
}
当我用 g++ O0 和 O2 运行 时,它会产生不同的结果: 使用 O2:
3
2
1
0
18446744073709551615
18446744073709551614
18446744073709551613
18446744073709551612
18446744073709551611
18446744073709551610
18446744073709551609
无 O2:
3
2
1
.....long time suspending
请问我对unsinged wrap around的理解有没有错? (我从几个 Whosebug post 和其他参考资料中了解到未标记的环绕是定义的行为)。
您是否知道一旦 global_w
递增到 3 然后 emplace()
中的 while
循环变成无限循环? AFAIK,无限循环导致 C++ 中的未定义行为。
我认为您的问题是因为您将 std::jthread
对象定义为临时对象。这意味着它们在它们出现的表达式末尾被破坏。因此,两个线程不会 运行 并行(同时)。
您可以通过创建线程变量轻松更改它,这些变量将在 main()
:
int main()
{
std::thread t1 ([]() -> void { // note that "t1" variable name
int i = 10;
while (i-- >= 0) emplace();
});
std::thread t2 ([]() -> void { // note that "t2" variable name
int i = 10;
while (i-- >= 0) pop();
});
}
然而,即便如此,我认为您在 global_r
上存在 数据竞争 ,这会导致 未定义的行为 作为出色地。如果没有它的同步写入,编译器可能很容易假设 emplace()
具有对 global_r
的独占访问权,并有效地从循环中“删除”此读取 local_r = global_r;
。
此类问题的现场演示:https://godbolt.org/z/566WP9n36。