std::memory_order_relaxed 是否足以检查 "Availability"?
Is std::memory_order_relaxed enough for checking "Availability"?
我有一个并发对象,它可能每次都保存指向函数的指针。对象的架构如下所示:
struct ConcurrentObject{
//variables
std::atomic<void(*)()> callback;
}
因此一个线程可能会决定他想要将回调附加到此对象并将其向前传递:
ConcurrentObject* co = new ConcurrentObject(); //I'm using smart pointers, no worries.
//do some logic
co->callback = someCallback; //void(*)() , this may be difference callback every time
修改后我得到这个对象并检查回调是否可用:
auto co = aquireConcurrentObject();
auto callback = co->callback.load();
if (callback){
callback()
}
现在,我们知道在不指定任何内存顺序的情况下,传递的默认内存顺序是 memory_order_seq_cst
,它告诉编译器(简而言之)"do not scrumble any read or write instruction to make the program faster, keep the relative order of instruction as specied by the code, and make it visible through the cpu's".
我们也知道这是一个很大的性能障碍,因为编译器对其可以采取的操作有更多的限制。
我的问题是 - std::memory_order_relaxed
是否足以执行此操作?
是的,您是对的,在您的示例中 std::memory_order_relaxed 可以安全使用,因为您的代码仅依赖于回调是原子的这一事实。您的代码不受内存操作可能重新排序的影响
您有衡量性能影响的方法吗?更改内存顺序在这里可能看起来是一个很好的性能引导(似乎有效),但实际上,在大多数应用程序中 - 这无关紧要。
在这里更改内存模型意味着维护此代码的任何人都需要格外小心,不要破坏它,因此这是您在做此类事情时面临的另一个风险。
此类优化需要在被证明是性能瓶颈后进行详细记录和选择。非必要请勿乱动
回调指针访问的内存顺序影响回调使用的 "visibility" 个变量。
如果您的回调:
1) 类似于 constexpr,也就是说它除了参数和常量全局变量外不使用任何东西,或者
2) 仅使用变量,这些变量在 (happens-before) 可能使用回调之前 初始化,
然后使用 std::memory_order_relaxed
存储和加载都可以。
但是如果你在//do some logic
下的代码初始化了一些回调使用的变量,那么你应该至少使用 std::memory_order_release
/std::memory_order_acquire
用于相应地存储和加载。否则,回调的执行可能会看到这些未初始化的变量(更严格地说,从 C++11 标准的角度来看,这将是 data race,即未定义的行为)。
我有一个并发对象,它可能每次都保存指向函数的指针。对象的架构如下所示:
struct ConcurrentObject{
//variables
std::atomic<void(*)()> callback;
}
因此一个线程可能会决定他想要将回调附加到此对象并将其向前传递:
ConcurrentObject* co = new ConcurrentObject(); //I'm using smart pointers, no worries.
//do some logic
co->callback = someCallback; //void(*)() , this may be difference callback every time
修改后我得到这个对象并检查回调是否可用:
auto co = aquireConcurrentObject();
auto callback = co->callback.load();
if (callback){
callback()
}
现在,我们知道在不指定任何内存顺序的情况下,传递的默认内存顺序是 memory_order_seq_cst
,它告诉编译器(简而言之)"do not scrumble any read or write instruction to make the program faster, keep the relative order of instruction as specied by the code, and make it visible through the cpu's".
我们也知道这是一个很大的性能障碍,因为编译器对其可以采取的操作有更多的限制。
我的问题是 - std::memory_order_relaxed
是否足以执行此操作?
是的,您是对的,在您的示例中 std::memory_order_relaxed 可以安全使用,因为您的代码仅依赖于回调是原子的这一事实。您的代码不受内存操作可能重新排序的影响
您有衡量性能影响的方法吗?更改内存顺序在这里可能看起来是一个很好的性能引导(似乎有效),但实际上,在大多数应用程序中 - 这无关紧要。
在这里更改内存模型意味着维护此代码的任何人都需要格外小心,不要破坏它,因此这是您在做此类事情时面临的另一个风险。
此类优化需要在被证明是性能瓶颈后进行详细记录和选择。非必要请勿乱动
回调指针访问的内存顺序影响回调使用的 "visibility" 个变量。
如果您的回调:
1) 类似于 constexpr,也就是说它除了参数和常量全局变量外不使用任何东西,或者
2) 仅使用变量,这些变量在 (happens-before) 可能使用回调之前 初始化,
然后使用 std::memory_order_relaxed
存储和加载都可以。
但是如果你在//do some logic
下的代码初始化了一些回调使用的变量,那么你应该至少使用 std::memory_order_release
/std::memory_order_acquire
用于相应地存储和加载。否则,回调的执行可能会看到这些未初始化的变量(更严格地说,从 C++11 标准的角度来看,这将是 data race,即未定义的行为)。