2014-09-03 27 views
7

我與(的供應商分公司)GCC 4.6的工作,並且需要在一個無符號整數原子GET /負載,並與舊GCC __sync內建設置/存儲

  • 單位遞增
  • 4個基本原子操作
  • 原子遞減
  • 原子設置
  • 原子得到

。這個gcc版本不支持較新的__atomix_XXX內置函數,只有__sync內置函數。

這意味着我可以做這些操作:

#define ATOMIC_INC(ptr) __sync_fetch_and_add((ptr),1) 
#define ATOMIC_DEC(ptr) __sync_fetch_and_sub((ptr),1) 
#define ATOMIC_GET(ptr) __sync_fetch_and_add((ptr),0) 

不過,我不能找到一種方法來實現的#define ATOMIC_SET(),這將原子設置一個變量,有沒有辦法用gcc 4.6 achiece這。X ?

另外,有沒有更好的方法來實現上述ATOMIC_GET()?從原子的角度來看,生成的程序集看起來很好,但由於實際執行了添加操作,所以它不是最優的。

編輯:有問題的體系結構是ARMv6,x86和x86_64。

+0

你在寫一些彙編語言可能是。根據所用的CPU架構,您的選擇根本不同。請明確說明。 – zwol 2014-09-03 15:07:57

+0

你也可以使用像ConcurrencyKit相對輕量級庫:http://concurrencykit.org/doc/ck_pr_store.html – 2014-09-03 17:32:37

+0

你有'__sync_lock_test_and_set'? – zch 2014-09-03 18:26:28

回答

6

__sync_xxx()是模仿一些英特爾原語,在你的x86上,原子加載/存儲是相當微不足道的,我認爲這是爲什麼該設置看起來不完整。

對於原子商店,我覺得你被卡住__sync_val_compare_and_swap(),雖然喜歡__sync_fetch_and_add()負載,這顯然是矯枉過正:-(

有是「整個內存屏障」 __sync_synchronize(),但我一直沒能發現這是什麼(除了在x86_64上的實驗)!如果你確切知道你正在編譯什麼機器,你可以玩得開心,看到...開始加載和存儲包裝在__sync_synchronize()

我可以告訴對於x86和x86_64,原子加載不需要任何額外的普通讀取操作。原子存儲需要一個mfence,如果你想要memory_order_seq_cst,但不是。但是... __sync_xxx系列中缺少的其他東西是編譯器障礙...除非這就是__sync_synchronize()實際上所做的!


後來添加的...

我建議C/C++11 mappings to processors對於如何原子能可/應在x86/x86_64的,ARM和PowerPC實現一個很好的說明。

要使用__sync_val_compare_and_swap()int的原子店:

void a_store(int* p_ai, int val) 
    { 
    int ai_was ; 

    ai_was = *p_ai ; 
    do { ai_was = __sync_val_compare_and_swap (p_ai, ai_was, val) ; 
    } ; 

在您的x86/x86_64的,對於memory_order_seq_cst(SC),你需要或者LOCK XCHGMOV後跟MFENCE ...等等在循環中使用LOCK CMPXCHG會有點痛苦。對於ARM,這也是有點痛苦,但更:-(


手卷原子加載/存儲是嚴格勇敢(或愚蠢)...和,取決於什麼!__sync_synchronize()實際上沒有一個給定的機器上,可能會或可能無法正常工作

所以,瑣碎的做法是:

__sync_synchronize() ; 
    v = v_atomic ;   // atomic load ! 
    __sync_synchronize() ; 

    __sync_synchronize() ; 
    v_atomic = v ;   // atomic store ! 
    __sync_synchronize() ; 

其中,用於x86/x86_64的編譯(對我來說,在GCC 4.8對於x86_64):

mfence 
    mov xxx, xxx 
    mfence 

用於加載和存儲。這是絕對安全(和SC)...對於加載它可能會或可能不會比LOCK XADD ...更好的存儲它可能會比LOCK CMPXCHG和周圍的迴路更好!

如果(且僅當)爲ARM編譯這給:

dmb 
    ldr/str 
    dmb 

然後,爲了安全(和SC)。

現在...適用於x86/x86_64的爲處理器你不需要任何MFENCE在所有負載,甚至不SC。但是你確實需要停止編譯器重新排序的東西。 __sync_synchronize()這樣做,以及種植mfence。對於GCC,你可以構建一個__sync_compiler()與下面的Voodoo:

#define __sync_compiler() __asm__ __volatile__("":::"memory") 

我天真地認爲,__sync_synchronize()(用於x86/x86_64的)實際上是:

#define __sync_mfence() __asm__ __volatile__("mfence":::"memory") 

因爲x86/x86_64的是這麼不乖你可以:

__sync_compiler() ; 
    v = v_atomic ;   // atomic load -- memory_order_seq_cst 
    __sync_compiler() ; 

    __sync_compiler() ; 
    v_atomic = v ;   // atomic store -- memory_order_seq_cst 
    __sync_synchronize() ; 

AND ...如果你能memory_order_release生活,那麼你可以通過一個更換僅存!現在

,爲ARMv7的......如果(且僅當 - 我沒有手臂,所以不能對此進行測試)__sync_synchronize()編譯爲dmb,那麼我們可以做負載非常輕微更好:

__sync_compiler() ; 
    v = v_atomic ;   // atomic load 
    __sync_synchronize() ; 

對於所有內存命令:memory_order_seq_cst和_acquire(和_consume)。

而對於memory_order_release我們可以:

__sync_synchronize() ; 
    v_atomic = v ;   // atomic store -- memory_order_release 
    __sync_compiler() ; 

對於似乎有特殊LDASTL指令ARMv8 ...但我有些在這裏我的深度。

注意:這是以下C/C++11 mappings to processors我有信心,但不能證明ARM的真相。

反正......如果你願意手工卷的原子加載/存儲,那麼你可以做的更好。

所以......如果這些東西速度真的很重要,我會忍不住用手卷,假設目標體系結構的限制數量,並指出:

  • 您使用無論如何gcc特定的東西,所以__sync_compiler()技巧不會引入額外可移植性問題。

  • __sync_xxx家族一直在GCC更完整__atomic_xxx所取代,所以如果你需要添加另一個目標架構未來,那麼你可能再能升級到__atomic_xxx

  • ,並在不太遠的將來distand標準C11原子公司將全面上市,所以解決可移植性問題,可以踢。

+0

__sync_synchronize()是一個存儲器屏障(例如產生mfense指令),並且__sync_xxx文檔似乎暗示他們也存儲器屏障。我將如何使用__sync_val_compare_and_swap來實現商店? – 2014-09-04 07:06:30

+0

@wrenchcode ...有點擴大了答案。 – 2014-09-07 09:58:12