memory consistency

目前的計算機系統中,都是shared memory結構,提供統一的控制接口給軟件,編程

shared memory結構中,爲了memory correctness,能夠將問題分爲:memory consistency,和memory coherency緩存

爲了memory consistency的正確性,是須要program的編寫者的操做,主要描述對一塊memory的不一樣的load,store之間的順序多線程

而memory coherency,則對software是徹底透明的,主要爲了多cache在系統中的表現像單cache同樣。架構

memory consistency實現以後,coherency必定是保證的,可是coherency保證以後,consistency不必定能夠保證。併發

 

爲了不memory consistency,須要保證對同一address的,多個core或者多個線程store/load都是 in-ordered,爲不一樣的address,無所謂的。app

  發生memory consistency,多是store-store,load-load,load-store。store-load之間的out-of order。性能

 

memory consistency model是一個系統性的問題,會直接影響程序(多線程,多進程)編寫fetch

  涉及到,processor的設計,memory system的設計,interconnect的設計,優化

      compiler的優化,programming language的使用。atom

關於consistency model最簡單的描述是,讀操做須要返回最新寫入memory的值

 

memory reorder只有在軟件實現lock-free 編程的時候,纔可能影響運算結果

memory ordering模型,是併發程序設計的基礎。主要的內容:

1) atomic VS reorder

  與cpu bus總線對齊的read/write,都是atomic的,若是非對齊,好比32bit的bus總線,訪問64bit的數據,

    映射到str,ldr會變爲兩條指令在單核多線程和多核的運行環境下,可能會出現half-read,half-write

    狀況。

  RMW,read-modified-write,都是非atomic的

 

  爲此不少的微處理器,提出了atomic的指令,一條指令實現較複雜的功能,從而實現atomic性;

    CMPXCHG(compare-and-exchange),XCHG等。這些atomic指令,只能保證單核多線程的原子性,在多核狀況下,仍是須要加lock

 

  reorder,的主要考慮是performance,在BP失敗或者cache miss的時候,都須要reorder,主要是STR或者LDR指令的亂序

      reorder的原則是硬件隱藏細節,使得數據的load,store,按single thread的順序正確執行

  reorder的類型包括,

      Compiler Reordering,程序編譯期間,

        Barrier指令保證先後的語句不會被reorder。

        compiler memory barrier instruction:GNU編譯器,asm volatile("":::"memory")

                               __asm__ __volatile(""::::"memory")

                         intel ecc compiler

                               __memory_barrier()

                         Microsoft Visual C++

                               ——ReadWriteBarrier() 

        CPU並感知不到compiler memory barrier的存在,並且實現compiler的memory barrier以後,

          reorder仍是可能在CPU memory reorder中發生的。

      CPU memory Ordering,程序執行期間:memory reorder,特指processor經過load,store指令,

          system bus訪問system memory期間的亂序

          memory reorder的類型能夠分爲四類:

                LoadLoad,

                LoadStore,

                StoreLoad,

                StoreStore,

          CPU  memory order的實現,對軟件來講,是透明的。 

      CPU會進行reorder的緣由 ,多是指令的BP失敗,cache miss等,先將load,store放在load buffer,store buffer,invalid buffer中

2) Memory Barrier

  cpu的memory barrier,相比較於compiler來講,是一條真正的指令,針對四種memory reorder,目前有四種memory barrier的類型:

    LoadLoad Barrier

    LoadStore Barrier

    StoreLoad Barrier

    StoreStore Barrier

  具體的barrier指令有三種:

    Store Barrier(Write Barrier),Store Barrier先後的store操做,必須是按順序執行的,對load沒有影響。

    Load Barrier(Read Barrier),Load Barrier先後的load操做,必須是按順序執行的,對write沒有影響。

    Full Barrier(Load+Store Barrier),Full Barrier兩邊的任何操做,均不能夠交換順序。

    

 

CPU的memory model根據processor,tool chain的不一樣,有不少的選擇。

  主要的CPU memory models:

    Programming Order------------------Strong Memory Model

    Sequential Consistency(SC模型)

    Strict Consistency

    Data Dependency Order------------Weaker Memory Model

  

 

強弱內存模型的主要區別點:但一個CPU核執行一連串的寫操做時,其餘的CPU核看到這些值的改變順序是否與其順序一致

  目前很難找到一個在硬件層面保證SC模型,因此當用上語言編程時,SC爲成爲一個重要的軟件內存模型,

  Java5以後,用violation聲明共享變量,

  C++11中,使用默認的順序約束memory_order_seq_cst,作原子操做,

  使用這些技術以後,編譯器會限制編譯亂序,而且插入特定CPU的memory barrier指令,來保證原子操做

 

最具典型的consistency model被稱爲SC (sequential consistency)。

sequential model在實現中的architecture:

1)系統中不帶cache的SC model,若是是多核,須要本身實現一個switch:

   

2)系統中帶有cache:多核狀況下,cache coherence protocol充當了switch的角色,能夠看作一個黑盒:

    

 

總結:

load-load、store-store,是必須按program order來的

load-store、store-load,是必須按program order來的

sequential consistency必須保證:1)program order,processor必須保證前一memory訪問結束,才能開始新的memory訪問;

                  在cache的系統中,必須保證全部的cache都被invalid或者update,收到相應的ack信號。  

               2)write atomicity,對同一地址的寫操做,必須是serialized,一樣必須是收到invalid和update的ack信號。

    這些要求,針對任何load-store對。

SC Ordering Rule總結,表示的是單核的要求,RMW表示原子操做。

    

在硬件優化與SC模型中會出現的問題:

  1) 沒有cache的狀況下,

    1.帶write buffer的結構,read bypass的操做下,後邊的讀操做可能會越過寫操做。

    2.重疊寫,overlapping write,多個不一樣地址的同時寫,後邊的寫操做,較前邊的寫操做,先完成。

    3.非阻塞讀,nonblocking reads,多個不一樣地址的同時讀,後邊的讀操做,較前邊的讀操做,先完成。

  2) 有cache的狀況下,

    1.必須實現cache coherency協議。

    2.檢查寫的ack信號。一個core必須等到另外一個core的寫ack信號以後,再發送下一個寫操做。

    3.保證write atomicity,cache的本質是將一個值的改變,傳播給多個處理器的緩存,自己是非原子性的,因此有兩個要求;

      1.針對同一地址的寫操做被串行化(serialized)

       2.對一個新寫的值的讀操做,必須是全部其餘的core,都返回ack的狀況。

  與特定的硬件優化結合起來的時候,SC模型的保證是有代價的。

 

其餘的memory model,較SC,有兩方面的改變:

  1) 放鬆對程序次序的要求,只適用於不一樣地址的load,store操做;

  2) 放鬆對原子性的要求,一些模型容許讀操做在「一個寫操做未對全部處理器可見的狀況下」執行。只適用於有buffer的體系結構。

 

X86多使用的是一種稱爲TSO(Total Store Order)的memory consistency model, 與SC model相比較。放開了store-load的順序要求

  只是放開了程序次序中的這一小部分

  TSO中,每一個processor都加入了一個write buffer,因此能夠memory order上來看store比load晚,可是指令上仍然是load先執行的

    相似於cache中的write-back的policy。

    

TSO model的實現:

    

  若是programmer要求,store-load必須按順序執行,則須要在store以後,加入fence命令。

    fence命令只能擋住本processor的執行,對其餘的processor起不到做用

    可是原子操做能夠,用exclusive的訪問,其餘的processor訪問不到該數據

TSO Ordering Rules要求,針對單核的狀況:

    

 

Relaxed memory models:

relaxed model既有對program order方面的優化也有write atomicity方面的優化

ARM和PowerPC,多使用relax model,一個example model,XC model,

XC model的ordering Rule,單核狀況:

    

XCmodel的實現:

    

 

程序設計時的memory相關的指令:

1) violatile,變量修飾詞,表示cpu不會每次都從cache中拿值,而是每次都必須訪問main-memory。通常計算機中的外設相關的變量都

    須要設定爲violatile,防止被compiler優化。可是並不能用於構建原子操做。

2) 原子操做,分爲blocking和non-blocking兩種,相似於AXI中的lock trans和exclusive trans。

3) 互斥鎖,mutex,在多進程,多線程間使用,拿不到mutex時,進程或線程被休眠blocking

    底層使用lock transaction。

4) 自旋鎖(spinlock),在等待共有資源有效的過程當中,會一直等待,線程、進程不會被休眠non-blocking

5) semaphore,等待必定數量的共有資源,直接在程序中聲明。blocking

    底層使用lock transaction。

6) read-modify-write,使用strex或者ldrex彙編指令,對讀改寫進行包裝。non-blocking

7) compare-and-swap,等同於LL(load-link)、SC(store-conditional)指令,底層也是調用strex和ldrex指令,non-blocking

8) 其餘的atomic operation,test-and-set,fetch-and-add,compare-and-swap指令。

 

在exclusive trans設計中,會有兩個monitor,一個local monitor(只是monitor該cpu,通常放在cpu core內),

    一個global monitor(monitor整個sys,通常放在主的interconnect中)

    

  bus trans的memory type若是是Non-Shareable的,此時的exclusive只檢查local monitor,而後可能會直接返回Exok

               若是是Shareable的,此時的exclusive,會檢查local monitor和global monitor,若是有violation,會直接返回Exok。

  在有cache的系統中,而且此時的memory type是cacheable的,exclusive的訪問須要和local minitor,global monitor,SCU共同決定sync的結果。

 

這些model在實現中,都是依託於指令集來實現的,arm和intel都有提供相應的原子操做指令。

 

在程序代碼編寫的時候,也能夠本身控制,經過memory fence命令,來保證多核多線程對共享變量的訪問。

單核系統中,也會出現memory consistent的問題,由於目前的cpu都是超標量,多線程,亂序執行的,因此program order並不能

    反映到executed order。

 

之上是單核memory reorder的問題,以後寫一些多核數據一致性的問題。

 

軟件中數據的同步問題,

1.在單核多線程的狀況下,能夠經過disable全部中斷,包括cpu內核調度,來達到目的,只在sync var被處理以後,再放開中斷。

    這樣能夠達到sync的目的,可是在增長了interrupt的latency。

2.在多核多線程的狀況下,須要保證多核之間的同步,arm增長了本身的指令,來鎖住bus,v6架構以前是swp指令,v6架構以後是ldrex指令。

 

下邊討論一下指令加鎖的應用:

 X86的不少指令,均可以在前邊加lock,來保證原子性,加lock的指令,會保證對當前訪問的內存的互斥性。

  在單核處理器系統中,可以在單條指令中完成的操做均可以被認爲是原子操做。由於中斷和線程切換都只能在指令與指令之間;

  可是在多核處理器中,因爲多個core同時獨立的運行,即便能單條指令就能夠完成的操做,也會受到其餘core的干擾,

    致使memory consistent出現問題,因此須要多核之間的同步。

X86的lock指令,作的即是多核之間訪問同一memory的同步,相似的有arm中的strex和ldrex,

arm中的strex和ldrex,是armv6以後才引進來的,以前使用的是swp,swpb指令,等效於lock bus+memory swap。

  缺點有兩點:

    1.可是直接鎖死bus會致使,其餘的core不能訪問bus,性能受到影響,

    2.因爲這個指令既有str又有ldr,多以執行時間會比較長,若是這時有中斷,就必須等待這條指令執行結束,這樣增長了中斷的latency。

  因此不管是多核仍是單核,swp指令都沒有目前的ldrex和strex方便。

 

參考文檔,Shared Memory Consistency Models A Tutorial.pdf

     a primer on memory consistency and coherence.pdf

     Weak vs. Strong Memory Models  網頁

        淺談Memory Reordering 網頁  

     CPU cache and Memory Ordering 併發程序設計入門

     https://www.kernel.org/doc/Documentation/memory-barriers.txt

相關文章
相關標籤/搜索