Atomic Operation 是甚麼?原子操作?

剛好在研究 Atomic Operation
因此整理了一下常見的 Atomic Operation
最小操作 (Atomic Operation)
Atomic Operation 代表這個操作是最小的操作,也就是說,當電腦在執行操作時,不會有其他人打斷這個操作,一次就全部完成。
接下來就來介紹各種不同的 Atomic Operation 吧!
Atomic Operation 如果直接翻譯的話是原子操作啦,但好像翻成最小操作比較直覺(?)
Compare and Swap (CAS)
會先比較變數「當前的值」和「期望的值」是否相同,如果相同,則寫入「新的數值」。
void compare_and_swap(int* var, int expect_val, int new_val){
if(*var == expect_val){
*var = new_val;
}
}
GCC 內建有提供 CAS 的 function,共有兩種:
__sync_bool_compare_and_swap
:回傳值是bool
,如果有成功被修改為newval
,回傳true
,反之則回傳false
。__sync_val_compare_and_swap
:回傳*ptr
在操作執行前的數值。
bool __sync_bool_compare_and_swap (type *ptr, type oldval type newval, ...)
type __sync_val_compare_and_swap (type *ptr, type oldval type newval, ...)
GCC 在 4.7.0 版本後,依據 C++11 的 memory model 有對 Atomic operator 更新1,但舊版的還是可以用。
更新後加入了成功和失敗的時候所採用的 memory order,如果不知道的話就用 __ATOMIC_SEQ_CST
。
另外也加入了 weak 和 strong 的類型,weak 的版本可能會 fail spuriously,但速度較快,相反地,strong 的版本不會發生 fail spuriously,但速度較慢,根據 GCC 文件,很多平台只提供 strong 的類型,所以如果不知道要用甚麼就用 strong 吧。
fail spuriously:即便變數當前的數值和期望的一致,仍然有機會回傳 false,這可能與實作方式有關,例如有些架構上使用 LL/SC 來實作 CAS,在 context switches 或是 cache flush 後,SC 依然有可能認為 LL 的變數有被動過,因此就會回傳 false。
__atomic_compare_exchange_n
和 __atomic_compare_exchange
的差別只在 desired
是不是指標。
bool __atomic_compare_exchange_n (type *ptr, type *expected, type desired, bool weak, int success_memorder, int failure_memorder)
bool __atomic_compare_exchange (type *ptr, type *expected, type *desired, bool weak, int success_memorder, int failure_memorder)
C++11 標準中也有 CAS,在 <atomic>
,其中分別有 weak 和 strong 的版本,explicit
則是有明確指定 memory order,沒有明確指定的就是 memory_order_seq_cst
。
atomic_compare_exchange_weak
atomic_compare_exchange_weak_explicit
atomic_compare_exchange_strong
atomic_compare_exchange_strong_explicit
Load Link / Store Conditional (LL/SC)
LL/SC 是兩個 CPU 指令,就是 Load Link (LL) 和 Store Conditional (SC)。
LL 會從記憶體讀取目標資料到暫存器,也就是得到了目標的當前值,SC 會將新的數值寫回原本的目標,如果該目標從執行 LL 到 SC 之間沒有任何修改的話。
在 MIPS 中有提供 LL/SC。
Test and Set (TSL)
TSL 會將數值設定為 set (非 0 數值),並回傳操作前的舊值 (0 or 非 0 數值),用 C++ 寫的話就是如下:
int test_and_set(int* var){
int old_val = *var;
*var = 1;
return old_val;
}
使用 Test and Set 可以輕鬆地實作 spin lock,在一些等待時間不長的情況下,可以避免 content switch 所帶來的成本。
舊版的 GCC 函式中是根據 Intel Itanium Processor-specific Application Binary Interface 所訂製的,裡面沒有這邊描述的 Test and Set2,因此可以用 CAS 模擬:
#define __sync_test_and_set __sync_val_compare_and_swap(ptr, 0, 1)
更新過的函式,一樣支持了 memory order,如果原本就是 set (非 0 數值),則回傳 true
,反之則是 false
:
bool __atomic_test_and_set (void *ptr, int memorder)
C++11 標準中在 <atomic>
也有提供 TSL,explicit
則是有明確指定 memory order,沒有明確指定的就是 memory_order_seq_cst
。
atomic_flag_test_and_set
atomic_flag_test_and_set_explicit
Fetch and Add (FAA)
將目標數值加上新的數值,如果用 C++ 寫起來就是以下的樣子:
int fetch_and_add(int* var, int val){
int old_val = *var;
*var += val;
return old_val;
}
如果你的操作只是對原本的數值做一些操作,則直接使用 FAA 會比較有效率,畢竟不用 CAS + 迴圈。
舊版的 GCC 函式有提供除了 Add 之外的操作,像是 add、sub、and…,回傳操作前的舊值:
type __sync_fetch_and_add (type *ptr, type value, ...)
type __sync_fetch_and_sub (type *ptr, type value, ...)
type __sync_fetch_and_or (type *ptr, type value, ...)
type __sync_fetch_and_and (type *ptr, type value, ...)
type __sync_fetch_and_xor (type *ptr, type value, ...)
type __sync_fetch_and_nand (type *ptr, type value, ...)
上方的是回傳舊值,GCC 也提供回傳新值的:
type __sync_add_and_fetch (type *ptr, type value, ...)
type __sync_sub_and_fetch (type *ptr, type value, ...)
type __sync_or_and_fetch (type *ptr, type value, ...)
type __sync_and_and_fetch (type *ptr, type value, ...)
type __sync_xor_and_fetch (type *ptr, type value, ...)
type __sync_nand_and_fetch (type *ptr, type value, ...)
更新後的版本也有提供 FAA 和 memory order:
type __atomic_fetch_add (type *ptr, type val, int memorder)
type __atomic_fetch_sub (type *ptr, type val, int memorder)
type __atomic_fetch_and (type *ptr, type val, int memorder)
type __atomic_fetch_xor (type *ptr, type val, int memorder)
type __atomic_fetch_or (type *ptr, type val, int memorder)
type __atomic_fetch_nand (type *ptr, type val, int memorder)
上方的是回傳舊值,下方回傳新值的:
type __atomic_add_fetch (type *ptr, type val, int memorder)
type __atomic_sub_fetch (type *ptr, type val, int memorder)
type __atomic_and_fetch (type *ptr, type val, int memorder)
type __atomic_xor_fetch (type *ptr, type val, int memorder)
type __atomic_or_fetch (type *ptr, type val, int memorder)
type __atomic_nand_fetch (type *ptr, type val, int memorder)
C++11 標準中在 <atomic>
也有提供 FAA 和其他相關的操作,explicit
則是有明確指定 memory order,沒有明確指定的就是 memory_order_seq_cst
。
atomic_fetch_add
atomic_fetch_add_explicit
atomic_fetch_sub
atomic_fetch_sub_explicit
atomic_fetch_and
atomic_fetch_and_explicit
atomic_fetch_or
atomic_fetch_or_explicit
atomic_fetch_xor
atomic_fetch_xor_explicit
Reference
如果你覺得這篇文章有用 可以考慮贊助飲料給大貓咪