關鍵字restrict

要理解 restrict,先要知道什麼是 Pointer aliasing。
Pointer aliasing 是指兩個或以上的指針指向同一數據,例如

  int i = 0;
  int *a = &i;
  int *b = &i; 

這樣會有什麼問題呢?
如果編譯器採用最安全的假設,即不理會兩個指針會否指向同一數據,那麼通過指針讀寫數據是很直觀的。
然而,這種假設會令編譯器無法優化,例如:

  int foo(int *a, int *b) 
  {     
  *a = 5;    
   *b = 6;     
   return *a + *b; 
   // 不一定是 11!
  }

如果 a 和 b 都指向同一數據,*b = 6 會導致 *a = 6,返回12。所以編譯器在做 *a + *b 的時候,需要重新讀取 *a 指向的數據:

   foo:     movl    $5, (%rdi)    # 存儲 5 至 *a   
     movl    $6, (%rsi)    # 存儲 6 至 *b     
     movl    (%rdi), %eax  # 重新讀取 *a (因爲有可能被上一行指令造成改變)     
     addl    $6, %eax      # 加上 6    
     ret 

如果我們確保兩個指針不指向同一數據,就可以用 restrict 修飾指針類型:

  int rfoo(int *restrict a, int *restrict b)
   {    
    *a = 5;     
    *b = 6;    
     return *a + *b;
   }  

編譯器就可以根據這個信息,做出優化:

   rfoo:    
    movl      $11, %eax   # 在編譯期已計算出 11     
    movl      $5, (%rdi)  # 存儲 5 至 *a     
    movl      $6, (%rsi)  # 存儲 6 至 *b    
    ret  

但如果用了 restrict 去修飾兩個指針,而它們在作用域內又指向同一地址,那麼是未定義行爲。
總之,restrict 是爲了告訴編譯器額外信息(兩個指針不指向同一數據),從而生成更優化的機器碼。注意,編譯器是無法自行在編譯期檢測兩個指針是否 alias。如使用 restrict,程序員也要遵守契約才能得出正確的代碼(指針不能指向相同數據)。 以個人經驗而言,編寫代碼時通常會忽略 pointer aliasing 的問題。更常見是在性能剖測時,通過反編譯看到很多冗餘的讀取指令,纔會想到加入 restrict 關鍵字來提升性能。

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