C++ atomic memory model和Arm實現方式

2021-03-02 MindShare思享

C++  atomic memory model  和 Arm構架實現

C++的memory model是軟體工程師比較難理解的一部分,因為深入理解它需要有一定的CPU構架和微構架的知識基礎。

C++作為高級語言本身應該和CPU的硬體是無關的,但是為了使一些原子(atomic)操作能夠在CPU更有效率的運行,避免因為原子操作帶來的memory ordering要求對系統性能的影響,C++的原子操作會帶有memory ordering的限定。

本文將通過圖表的方式介紹C++的atomic memory ordering和其在Arm構架上的實現。

閱讀本文需要有些C++atomic和CPU構架基礎。

§std::atomic 

 §The class to use when writing lock-free code! 

 §A template wrapper around various types, providing access that is: 

    Atomic – reads and writes are done as a whole. 

    Ordered with respect to other accesses to the variable (or others). 

§Depending on the target platform, operations can be lock-free, or protected by a mutex.

一個例子,

§Memory access ordering is specified by std::memory_order _... 

 §Operations can limit reordering around themselves for operations with the ordinary variables and operations with other atomic variables.


下面是對每個memory ordering specifier (std::memory_order _...)限定的memory ordering的具體解釋,綠色箭頭表示允許的re-order,紅色表示不允許的re-order. (圖表原創,all copy rights are reserved)




在不同的Arm構架上,C++的atomic的實現會有所不同。

在v7-A上是通過LDREX/STREX + DMB來實現的,在V8-A上通過LDREX/STREX + DMB和Load-acquire (LDRA), store-release(STRL)指令來實現。

在Armv8.1-A上增加了atomic指令,這些指令本身也可以帶acquire,release的memory ordering限定。

下面是Armv8.1-A的實現方法。




只有充分理解C++的memory model,和理解它在具體CPU的實現,才能更正常和有效的使用它們。

本文作為一個概述,有時間再寫更具體的內容。

相關焦點

  • 理解memory barrier
    要理解memory barrier必須先知道memory ordered(內存順序)的概念,接觸過一點底層架構的同學大概知道,x86是strongly-ordered,arm是weakly-ordered。
  • 走進C++11(三十七)原子操作之 std::atomic
    每個 std::atomic 模板的實例化和全特化定義一個原子類型。若一個線程寫入原子對象,同時另一線程從它讀取,則行為良好定義(數據競爭的細節我們會在下節--內存模型裡講解)另外,對原子對象的訪問可以建立線程間同步,並按 std::memory_order 所對非原子內存訪問定序。 需要注意的是,std::atomic 既不可複製亦不可移動。
  • 走進C++11(四十)最寬鬆的順序 memory_order_relaxed 內存模型(三)
    今天是最簡單也是最寬鬆的內存模型----memory_order_relaxed 帶標籤 memory_order_relaxed 的原子操作無同步操作;它們不會在同時的內存訪問間強加順序。它們只保證原子性和修改順序一致性。
  • 線程安全之std::atomic探索
    多線程的數據訪問一直很讓程式設計師們頭大,有什麼簡便方法來實現多線程間的通呢?
  • C++機器學習庫介紹
    它還包括線性代數和數值優化的功能。這些是在執行機器學習任務時非常重要的關鍵數學函數。我們將首先了解如何安裝Shark並設置環境。然後我們將用Shark實現線性回歸。安裝Shark和安裝環境(Linux)Shark依賴於Boost和cmake。
  • ARM棧回溯——從理論到實踐,開發IDA-arm-unwind-plugin
    ehabi 的回溯方式,再到 elf 文件中的 unwind 信息,最後實現一款 IDA 裡實時進行 arm 棧回溯的插件,覆蓋了現代 arm 棧回溯的全部內容,希望能給大家帶來幫助。查閱資料,隨著時代發展,arm 有兩種 unwind 方式:1. 一種是古老的,和 x86 類似的(目前沒有找到樣例,可能在某種編譯選項下存在),使用專用的 fp 寄存器保存原先的 sp,fp 在函數內禁止被改寫,thumb 模式下使用 r7 作為 fp,arm 模式下使用 r11 作為 fp。2.
  • C++機器學習庫介紹 | 文末送書
    +11 -lboost_serialization -lshark -lcblas用Shark實現線性回歸初始化階段#include <bits/stdc++.h> //所有c++標準庫的頭文件#include <shark/Data/Csv.h> //導入csv數據的頭文件#include <shark
  • xmake v2.3.8 發布, 新增 Intel C++/Fortran 編譯器支持
    雖然,倉庫的包維護工作量巨大,但是目前的發展趨勢也日趨活躍,我們收到了越來越多的用戶對倉庫包的貢獻和改進維護。並且,現在我們的官方倉庫已經可以快速集成:linux, macOS, windows, mingw, bsd, msys, iphoneos, android 等八大常用平臺的庫,實現真正的跨平臺 C/C++ 遠程依賴庫集成和使用支持。
  • ARM彙編和內嵌彙編
    反之如果立即數很大,超過了12bit的表示範疇,那麼就不能用一條mov指令了,畢竟arm指令最大只有32bit的空間可用(RISC的arm所有的指令長度是一致的,效率較高,當然我們並不關心16bit的thumb指令)。
  • 深入理解一致性與 C++ 內存模型
    Consistency model上圖摘自 https://jepsen.io/consistency,這是一個對一致性比較好的分類和總結。一般通過 Two-Phase Locking Protocol 來實現,另外主流資料庫也都是 SS2PL 的方式來實現,這一點可以參考 Wikipedia Two-Phase Locking。
  • x=1;std::cout<
    最簡單的模型就是只簡化內存操作為Store(內存存儲)和Load(內存讀取)這兩個操作,因此2×2可以組合出至少以下4種序列。c++11定義的模型比這個要更複雜,大概有以下full memory barrier(mb)release memory barrier(relb)acquire memory barrier(acqb)write memory barrier(wb)read memory barrier(rb)
  • arm-linux-ld命令 ld連結腳本
    我們看它是如何編譯的,注意我們這裡使用的不是arm-linux-gcc而是arm-elf-gcc,二者之間沒有什麼比較大的區別,arm-linux-gcc可能包含更多的庫文件,在命令行的編譯上面是沒有區別。
  • arm串口實現 printf 和scanf函數
    問題描述:arm串口實現scanf和printf函數實現輸入一串十進位數字換成十六進位輸出功能,但是我的板子測試就是出不來#ifdef __ARMEB__#define __xh "r0"#define __xl "r1"#else#define __xl "r0"#define __xh "r1"#endif這段代碼的意思是如果定義了arm的endbig則用32位的r0寄存器保存x的高位
  • GCC 10 已添加對 C++20 協程的實驗性支持
    有關 C++ 協程的示例語法和更多詳細信息請在 cppreference.com 上查看。可以看到,官方對協程的定義是:A coroutine is a function that can suspend execution to be resumed later. 協程是能暫停執行以在之後恢復的函數。
  • LWN 翻譯:Atomic Mode Setting 設計簡介(下)
    但是不管怎樣,這兩種更新方式都是以「要麼全有或要麼全無」的原子方式來更新顯示狀態的,並且這兩種方式都可以使用同一個「property change list」進行 ioctl() 操作。因此,單從用戶空間和 kernel 之間傳遞的數據結構組織形式來看(而不是從發起調用請求的語義來看),將它們視為同一種操作也是說得通的。
  • android平臺arm指令學習和調試
    force-modearm或者setarmforce-modethumb讓gdb切換thumb和arm代碼顯示。t=184375調試方法1)So中實現的方法,程序跑起來再附加的話,該方法可能已經執行完。看雪上給出方法步驟較多,應用和底層都調試,有點複雜如下:
  • Effective C++條款13 C++基本功之智能指針
    智能指針簡介C++語言標準庫STL提供了一套智能指針接口,且一直處於完善狀態,如下圖所示:最早的智能指針是std::auto_ptr,到c++11才開始廣泛使用,平時用得最多的是這三個:}這樣採用對象的方式來管理內存,通過對象的生命周期來自動釋放內存。
  • c++中的pair和tuple
    一、c++中的複雜數據結構從C開始,c++發展過來也一樣,在標準庫中一般不支持複雜的數據結構。比如多維的數組,多鍵值的KV,更不用說複雜的一批的樹了。其實也很好理解,c/c++作為一個底層支持語言,追求的是高效和多平臺支持,這就導致無法在複雜的數據結構中對整個數據結構進行極致優化,特別是針對於特定的平臺的極致優化。
  • C++、java 和 C 的區別
    的區分 ,這個跟java和c# 的區別比較大,但c#裡面有unit ulong ushort 這三種就相當於c++的修飾詞unsigned,當c++李明的變量類型定義unsigned,就默認是整數。2.java和c#裡面都有字符串型 和byte型, 但c++裡面沒有,但它是以另外的形式存儲這類型的數據的,比如 java和c#裡面的 byte其實就是unsigned char類型;c++中字符數組就能存儲字符串 (char a[]={"hello"}; ps:注意c++裡面定義數組 變量必須在中括號前面)。