簡體   English   中英

更便宜的 std::atomic 替代品<bool> ?

[英]Cheaper alternative to std::atomic<bool>?

我在多線程應用程序中有一類對象,其中每個線程都可以將對象標記為刪除,然后中央垃圾收集器線程實際刪除該對象。 線程通過訪問內部 bool 的成員方法進行通信:

class MyObjects {
...   
bool shouldBeDeleted() const
{
   return m_Delete;
}

void
markForDelete()
{
   m_Delete = true;
}
...
   std::atomic< bool >                                        m_IsObsolete;
}

過去,由於 Thread Sanitizer 一直在抱怨,因此其他人已將 bool 設為原子。 但是, perf 現在表明在內部原子負載期間存在處理開銷:

   │     ↓ cbz    x0, 3f4                                                                                                                                                                                                                                                                                                                                                                                            

   │     _ZNKSt13__atomic_baseIbE4loadESt12memory_order():                                                                                                                                                                                                                                                                                                                                                           

   │           {                                                                                                                                                                                                                                                                                                                                                                                                     

   │             memory_order __b = __m & __memory_order_mask;                                                                                                                                                                                                                                                                                                                                                       

   │             __glibcxx_assert(__b != memory_order_release);                                                                                                                                                                                                                                                                                                                                                      

   │             __glibcxx_assert(__b != memory_order_acq_rel);                                                                                                                                                                                                                                                                                                                                                      

   │                                                                                                                                                                                                                                                                                                                                                                                                                 

   │             return __atomic_load_n(&_M_i, __m);                                                                                                                                                                                                                                                                                                                                                                 

   │       add    x0, x0, #0x40                                                                                                                                                                                                                                                                                                                                                                                          

 86,96 │       ldarb  w0, [x0]  

目標平台為 GCC、Aarch64 和 Yocto Linux。

現在我的問題如下:

  • 在這種情況下真的需要原子嗎? bool 的轉換是一種方式(從 false 到 true),在對象存在期間無法返回,因此不一致僅意味着稍后刪除對象,對嗎?

  • 是否有std::atomic<bool>的替代方案可以使 Thread Sanitizer 靜音,但在計算上比std::atomic<bool>便宜?

一個明顯的修改可能是指定memory_order_relaxed以最小化內存障礙。

請參閱https://en.cppreference.com/w/cpp/atomic/memory_order

https://bartoszmilewski.com/2008/12/01/c-atomics-and-memory-ordering/

另請參閱 Herb Sutter 的經典《原子武器》: https : //channel9.msdn.com/Shows/Going+Deep/Cpp-and-Beyond-2012-Herb-Sutter-atomic-Weapons-1-of-2

m_Delete.store (true, std::memory_order_relaxed);

警告請參閱上面的文章)-如果對被標記為刪除的對象存在任何共同依賴關系(例如另一個狀態變量、釋放資源等),那么您可能需要使用memory_order_release來確保can be deleted標記設置發生在最后和編譯器優化器不會重新排序。

假設“垃圾收集器”檢查can be deleted標志,則不需要在加載中使用memory_order_acquire 放松就足夠了。 否則,它需要使用獲取來保證在讀取標志之前不會重新排序任何相互依賴的訪問。

問題(如 OP 的評論中所澄清)不是真正的 GC,而是延遲刪除單獨線程上的對象,以便減輕主處理線程從刪除到刪除所需的時間。 所有要刪除的對象都會在某個時間被標記——稍后刪除線程出現並刪除它們。

首先考慮:是否真的需要延遲刪除才能滿足程序的性能目標——特別是延遲? 實際上影響延遲的可能只是額外的開銷。 (或者可能還有不同的性能目標,例如吞吐量,需要考慮。)延遲刪除並不是所有情況下的明顯性能優勢 - 您需要找出在每種情況下它是否合適。 (例如,它甚至可能不是所有刪除必須的:也許某些刪除可以在不影響性能的情況下立即執行,而其他刪除則需要延遲。這可能是因為,例如,不同的處理線程正在執行不同的操作不同的延遲/吞吐量要求。)

現在有一個解決方案:由於我們正在談論延遲刪除 - 刪除線程沒有理由需要掃描所有對象以查找要刪除的對象(每次進行完整掃描時)。 相反,在將對象標記為刪除時支付稍高的成本,而無需支付掃描所有對象的成本。 通過將已刪除的對象鏈接到刪除工作列表來執行此操作。 那里有一個同步成本(除了明顯的鎖之外,還可以通過各種方式將其最小化),但它是為每個對象支付一次,而不是每個對象每次掃描支付一次

(也不必是鏈表。如果在一段時間內可以刪除多少對象有上限,您可以使用適當的數組。)

通過將這個問題更准確地描述為“延遲刪除”而不是“垃圾收集”,還開辟了其他可能性:取消了一些限制(也許添加了其他限制)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM