欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

淺析C++?atomic?和?memory?ordering

 更新時間:2022年04月26日 10:18:45   作者:pokpok  
這篇文章主要介紹了C++?atomic?和?memory?ordering的相關(guān)知識,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下

如果不使用任何同步機制(例如 mutex 或 atomic),在多線程中讀寫同一個變量,那么,程序的結(jié)果是難以預料的。簡單來說,編譯器以及 CPU 的一些行為,會影響到程序的執(zhí)行結(jié)果:

  • 即使是簡單的語句,C++ 也不保證是原子操作。
  • CPU 可能會調(diào)整指令的執(zhí)行順序。
  • 在 CPU cache 的影響下,一個 CPU 執(zhí)行了某個指令,不會立即被其它 CPU 看見。

利用 C++ 的 atomic<T> 能完成對象的原子的讀、寫以及RMW(read-modify-write),而參數(shù) std::memory_order 規(guī)定了如何圍繞原子對象的操作進行排序。memory order 內(nèi)存操作順序其實是 內(nèi)存一致性模型 (Memory Consistency Model),解決處理器的 write 操作什么時候能夠影響到其他處理器,或者說解決其他處理處理器什么時候能夠觀測到當且 寫CPU/寫線程 寫入內(nèi)存的值,有了 memory odering,我們就能知道其他處理器是怎么觀測到 store 指令的影響的。

一致模型有很多種,在 Wikipedia 里面搜索 Consistency model 即可看到,目前 C++ 所用到有 Sequential Consistency 和 Relaxed Consistency 以及 Release consistency。

Memory Operation Ordering

我們所編寫的程序會定義一系列的 loadstore 操作,也就是 Program ordering,這些 load 和 store 的操作應用在內(nèi)存上就有了內(nèi)存操作序(memory operation ordering),一共有四種內(nèi)存操作順序的限制,不同的內(nèi)存一致模型需要保持不同級別的操作限制,其中 W 代表寫,R 代表讀:

  • W -> R:寫入內(nèi)存地址 X 的操作必須比在后面的程序定義序列的讀取地址 Y 之前提交 (commit), 以至于當讀取內(nèi)存地址 Y 的時候,寫入地址 X 的影響已經(jīng)能夠在讀取Y時被觀測到。
  • R -> R: 讀取內(nèi)存地址 X 的操作必須在后序序列中的讀取內(nèi)存地址 Y 的操作之前提交。
  • R -> W:讀取內(nèi)存地址 X 的操作必須在后序序列中讀取內(nèi)存地址 Y 的操作之前提交。
  • W -> W:寫入內(nèi)存地址 X 的操作必須在后續(xù)序列中寫入內(nèi)存地址 Y 的操作之前提交。

提交的意思可以理解為,后面的操作需要等前面的操作完全執(zhí)行完才能進行下一個操作。

Sequential consistency

序列一致是 Leslie Lamport 提出來的,如果熟悉分布式共識算法 Paxos ,那么應該不陌生這位大科學家,而序列一致的定義是:

  • the result of any execution is the same as-if (任何一種執(zhí)行結(jié)果都是相同的就好像)
  • the operations of all threads are executed in some sequential order (所有線程的操作都在某種次序下執(zhí)行)the operations of each thread appear in this sequence in the order specified by their program (在全局序列中的,各個線程內(nèi)的操作順序由程序指定的一致)

組合起來:全局序列中的操作序列要和線程所指定的操作順序要對應,最終的結(jié)果是所有線程指定順序操作的排列,不能出現(xiàn)和程序指定順序組合不出來的結(jié)果。

怎么做會違反 sequcential consistency(SC)?也就是 SC 的反例是什么?

  • 亂序執(zhí)行 (out-of-order)
  • 內(nèi)存訪問重疊,寫A的過程中讀取A,寬于計算機word的,64位機器寫128位變量

更加形象的理解可以從內(nèi)存的角度來看:

所有的處理器都按照 program order 發(fā)射 loadstore 的操作,而內(nèi)存一個地一個地從上面 4 個處理器中讀取指令,并且僅當完成一個操作后才會去執(zhí)行下一個操作,類似于多個 producer 一個 consumer 的情況。

(Lamport 一句話,讓我為他理解了一下午)

SC 需要保持所有的內(nèi)存操作序(memory operation ordering),也是最嚴格的一種,并且 SC 是 c++ atomic<T> 默認的以一種內(nèi)存模型,對應 std::memory_order_seq_cst,可以看到標準庫中的函數(shù)定義將其設置為了默認值:

bool
    load(memory_order __m = memory_order_seq_cst) const noexcept
    { return _M_base.load(__m); }

Relaxed Consistency

松弛內(nèi)存序,對應的 std::memory_order_relaxed,在 cppreference 上的說明是:"不保證同步操作,不會將一定的順序強加到并發(fā)內(nèi)存訪問上,只保證原子性和修改順序一致性",并且通常用于計數(shù)器,比如 shared_ptr 的引用計數(shù)。

松弛內(nèi)存序不再保證 W -> R,不相互依賴的讀寫操作可以在 write 之前或者在同一時間段并行處理。(讀內(nèi)存并不是想象中的那么簡單,有內(nèi)存尋址過程,將內(nèi)存數(shù)據(jù)映射到 cache block,發(fā)送不合法位用于緩存替換)

好處是什么?性能,執(zhí)行命令的寫操作的延遲都被抹去了,cpu 能夠更快的執(zhí)行完一段帶有讀寫的指令序列。

具體實現(xiàn)是通過在 cpu 和 cache 之間加入一個 write buffer,如下圖:

處理器 Write 命令將會發(fā)送到 Write Buffer,而 Read 命令就直接能訪問 cache,這樣可以省去寫操作的延遲。Write Buffer 還有一個細節(jié)問題,放開 W -> R 的限制是當 WriteRead 操作內(nèi)存地址不是同一個的時候,R/W 才能同時進行甚至 R 能提前到 W 之前,但如果 Write Buffer 中有一個 Read 所依賴的內(nèi)存地址就存在問題,Read 需要等在 Write buffer 中的 Write 執(zhí)行完成才能繼續(xù)嗎?只需要 Read 能直接訪問這個 Write Buffer,如下(注:這里的Load通常和Read等意,StoreWrite等意):

Release Consistency

在這種一致性下,所有的 memory operation ordering 都將不再維護,是最激進的一種內(nèi)存一致模型,進入臨界區(qū)叫做 Acquire ,離開臨界區(qū)叫做 Release。所有的 memory operation ordering 都將不再維護,處理器支持特殊的同步操作,所有的內(nèi)存訪問指令必須在 fence 指令發(fā)送之前完成,在 fench 命令完成之前,其他所有的命令都不能開始執(zhí)行。

Intel x86/x64 芯片在硬件層面提供了 total store ordering 的能力,如果軟件要求更高級別的一致性模型,處理器提供了三種指令:

  • mm_lfence:load fence,等待所有 load 完成
  • mm_sfence:store fence,等待所有 store 完成
  • mm_mfence:完全讀寫屏障

而在 ARM 架構(gòu)上,提供的是一種非常松弛(very relaxed)內(nèi)存一致模型。

PS. 曾經(jīng)有個公司做出了支持 Sequential Consistency 的硬件,但是最終還是敗給了市場。

Acquire/Release

Acquire/release 對應 std::memory_order_acquirestd::memory_order_acquire,它們的語義解釋如下:

  • Acquire:如果一個操作 X 帶有 acquire 語義,那么在操作 X 后的所有 load/store 指令都不會被重排序到操作 X 之前,其他處理器會在看到操作X后序操作的影響之前看到操作 X 的影響,也就是必須先看到 X 的影響,再是后續(xù)操作的影響。
  • Relase:如果一個操作 X 帶有 release 語義,那么在操作 X 之前的所有 load/store 指令操作都不會被重排序到操作 X 之后,其他處理器會先看到操作 X 之前的操作。

Acquire/Release 常用在互斥鎖(mutex lock)和自旋鎖(spin lock),獲得一個鎖和釋放一個鎖需要分別使用 Acquire 和 Release 語義防止指令操作被重排出臨界區(qū),從而造成數(shù)據(jù)競爭。

Acquire/Consume

Acquire/Consume 對應 std::memory_order_acquirestd::memory_order_consume,兩種內(nèi)存模型的組合僅有 consume 不同于 release,不同點在于,假設原子操作 X, Release 會防止 X 之前的所有指令不會被重排到 X 之后,而 Consume 只能保證依賴的變量不會被重排到 X 之后,引入了依賴關(guān)系。

但是在 cppreference 上面寫著,“釋放消費順序的規(guī)范正在修訂中,而且暫時不鼓勵使用memory_order_consume。”,所以暫時不對其做深入的研究。

Volatile

volatile 關(guān)鍵詞通常會被拿出來說,因為通常會在并發(fā)編程中被錯誤使用:

volatile 的翻譯是“不穩(wěn)定的,易發(fā)生變化的”,編譯器會始終讀取 volatile 修飾的變量,不會將變量的值優(yōu)化掉,但是這不是用在線程同步的工具,而是一種錯誤行為,cppreference上面寫道:“volatile 訪問不建立線程間同步,volatile 訪問不是原子的,且不排序內(nèi)存,非 volatile 內(nèi)存訪問可以自由地重排到 volatile 訪問前后。”(Visual Studio 是個例外)。

volatile 變量的作用是用在非常規(guī)內(nèi)存上的內(nèi)存操作,常規(guī)內(nèi)存在處理器不去操作的時候是不會發(fā)生變化的,但是像非常規(guī)內(nèi)存如內(nèi)存映射I/O的內(nèi)存,實際上是在和外圍設備做串口通信,所以不能省去。(《modern effective c++》)

到此這篇關(guān)于C++ atomic 和 memory ordering的文章就介紹到這了,更多相關(guān)C++ atomic 和 memory ordering內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C語言打印各種圖案實例代碼

    C語言打印各種圖案實例代碼

    大家好,本篇文章主要講的是C語言打印各種圖案實例代碼,感興趣的同學趕快來看一看吧,對你有幫助的話記得收藏一下,方便下次瀏覽
    2021-12-12
  • C++ list的實例詳解

    C++ list的實例詳解

    這篇文章主要介紹了 C++ list的實例詳解的相關(guān)資料,希望通過本文大家能夠理解掌握這部分內(nèi)容,需要的朋友可以參考下
    2017-09-09
  • 如何理解C++指針常量和常量指針

    如何理解C++指針常量和常量指針

    這篇文章主要介紹了如何理解C++指針常量和常量指針,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下
    2022-06-06
  • C++中String類型的逆序方式

    C++中String類型的逆序方式

    這篇文章主要介紹了C++中String類型的逆序方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-07-07
  • 用c語言根據(jù)可變參數(shù)合成字符串的實現(xiàn)代碼

    用c語言根據(jù)可變參數(shù)合成字符串的實現(xiàn)代碼

    本篇文章是對用c語言根據(jù)可變參數(shù)合成字符串的方法進行了詳細的分析介紹,需要的朋友參考下
    2013-05-05
  • C語言實現(xiàn)掃雷游戲的方法

    C語言實現(xiàn)掃雷游戲的方法

    這篇文章主要為大家詳細介紹了C語言實現(xiàn)掃雷游戲的方法,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • Visual Studio Code配置C、C++環(huán)境并編寫運行的方法

    Visual Studio Code配置C、C++環(huán)境并編寫運行的方法

    這篇文章主要介紹了Visual Studio Code配置C、C++環(huán)境并編寫運行的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-08-08
  • C++訪問注冊表獲取已安裝軟件信息列表示例代碼

    C++訪問注冊表獲取已安裝軟件信息列表示例代碼

    這篇文章主要介紹了c++通過讀取注冊表獲得本機已安裝軟件信息的方法,大家參考使用吧
    2013-11-11
  • C語言實現(xiàn)教務管理系統(tǒng)

    C語言實現(xiàn)教務管理系統(tǒng)

    這篇文章主要為大家詳細介紹了C語言實現(xiàn)教務管理系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • 使用C語言編寫一個強制關(guān)機程序

    使用C語言編寫一個強制關(guān)機程序

    這篇文章主要為大家詳細介紹了如何使用C語言實現(xiàn)一個簡單的"流氓軟件",一個可以強制關(guān)機惡作劇關(guān)機程序,輸入指定指令才可以解除,感興趣的小伙伴可以學習一下
    2023-11-11

最新評論