C/C++ restrict和Atomic關鍵字

restrict 關鍵字允許編譯器優化某部分代碼以更好地支持計算。說到底,就是更好地適應編譯器

只能 用於指針,表明該指針是訪問數據對象的唯一且初始的方式。要弄明白爲什 麼這樣做有用,先看幾個例子。考慮下面的代碼:

int ar[10];
int * restrict restar = (int *) malloc(10 * sizeof(int));
int * par = ar;

這裏,指針restar是訪問由malloc()所分配內存的唯一且初始的方式。因 此,可以用restrict關鍵字限定它。而指針par既不是訪問ar數組中數據的初始 方式,也不是唯一方式。所以不用把它設置爲restrict。

現在考慮下面稍複雜的例子,其中n是int類型:

for (n = 0; n < 10; n++)
{
par[n] += 5;
restar[n] += 5;
ar[n] *= 2;
par[n] += 3;
restar[n] += 3;
}

由於之前聲明瞭 restar 是訪問它所指向的數據塊的唯一且初始的方式, 編譯器可以把涉及 restar的兩條語句替換成下面這條語句,效果相同:

restar[n] += 8; /* 可以進行替換 */

但是,如果把與par相關的兩條語句替換成下面的語句,將導致計算錯
誤:

par[n] += 8; / * 給出錯誤的結果 */

這是因爲for循環在par兩次訪問相同的數據之間,用ar改變了該數據的
值。
在本例中,如果未使用restrict關鍵字,編譯器就必須假設最壞的情況 (即,在兩次使用指針之間,其他的標識符可能已經改變了數據)。如果用 了restrict關鍵字,編譯器就可以選擇捷徑優化計算。
restrict 限定符還可用於函數形參中的指針。這意味着編譯器可以假定在函數體內其他標識符不會修改該指針指向的數據,而且編譯器可以嘗試對 其優化,使其不做別的用途。例如,C 庫有兩個函數用於把一個位置上的字 節拷貝到另一個位置。在C99中,這兩個函數的原型是:

void * memcpy(void * restrict s1, const void * restrict s2, size_t n);
void * memmove(void * s1, const void * s2, size_t n);

這兩個函數都從位置s2把n字節拷貝到位置s1。memcpy()函數要求兩 位置不重疊,但是memove()沒有這樣的要求。聲明s1和s2爲restrict說明這兩 個指針都是訪問相應數據的唯一方式,所以它們不能訪問相同塊的數據。這 滿足了memcpy()無重疊的要求。memmove()函數允許重疊,它在拷貝數據時不得不更小心,以防在使用數據之前就先覆蓋了數據。
restrict 關鍵字有兩個讀者。一個是編譯器,該關鍵字告知編譯器可以 自由假定一些優化方案。另一個讀者是用戶,該關鍵字告知用戶要使用滿足 restrict要求的參數。總而言之,編譯器不會檢查用戶是否遵循這一限制,但 是無視它後果自負。

_Atomic類型限定符(C11)

併發程序設計把程序執行分成可以同時執行的多個線程。這給程序設計 帶來了新的挑戰,包括如何管理訪問相同數據的不同線程。C11通過包含可 選的頭文件stdatomic.h和threads.h,提供了一些可選的(不是必須實現的) 管理方法。值得注意的是,要通過各種宏函數來訪問原子類型。當一個線程 對一個原子類型的對象執行原子操作時,其他線程不能訪問該對象。例如, 下面的代碼:

int hogs;// 普通聲明
hogs = 12;  // 普通賦值
可以替換成:
_Atomic int hogs;      // hogs 是一個原子類型的變量
atomic_store(&hogs, 12);  // stdatomic.h中的宏

這裏,在hogs中儲存12是一個原子過程,其他線程不能訪問hogs。
編寫這種代碼的前提是,編譯器要支持這一新特性。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章