2017-02-28 36 views
6

FreeBSD's generic implementation of memchr做:爲什麼FreeBSD的memchr實現在它的條件下增加它的指針?

void * 
memchr(const void *s, int c, size_t n) 
{ 
    if (n != 0) { 
     const unsigned char *p = s; 

     do { 
      if (*p++ == (unsigned char)c) 
       return ((void *)(p - 1)); 
     } while (--n != 0); 
    } 
    return (NULL); 
} 

這對我來說似乎是不必要的複雜;最初的n != 0檢查與do - while只是爲了避免p聲明似乎完全沒有意義。然而,我在爲什麼循環體確實特別感興趣:

if (*p++ == (unsigned char)c) 
    return ((void *)(p - 1)); 

,而不是更直截了當:

if (*p == (unsigned char)c) 
    return ((void *) p); 
++p; 

是否內聯後遞增與病情有一定優化的好處對於一些編譯器/平臺?

+1

您是否檢查編譯器生成的機器碼(使用優化)?這與你的方法有什麼不同?'p'的_definition_是否生成代碼?是的,你會錯過考慮最可能編寫代碼的時間以及當時優化編譯器的優劣。相當多的CPU有「減量和分支如果不是零」指令。 – Olaf

+0

@Olaf我確實使用godbolt來比較實現和gcc(使用-O3,再次使用-Os)。我不擅長彙編,但直截了當的版本似乎產生較少的指令。 – jamesdlin

+0

重新閱讀我的評論,我增加了很多。你檢查每個運行BSD的目標? x86並不是一個廣泛傳播的平臺。該代碼甚至可以用於8位和16位MCU,其數量超過x86和ARM數十年。 – Olaf

回答

2

指針後遞增的情況下是允許的時代(例如PCC)的編譯器使用的DEC計算機自動遞增尋址模式(像馬克Plotnick在評論中提到)。

由於所有的DEC計算機支持自動增量尋址,編碼循環的這種方式曾經是很常見的(順便說一句,m68k的支持相同的優化)。

另一方面,do-while循環僅僅是因爲在naïf編譯器上它傾向於產生更好的代碼,而不僅僅是爲了避免設置p

也不應該做一個現代的編譯器有什麼區別。

+0

避免設置'p'的確保存了一條指令,當然是 –

+0

對於naïf編譯器來說,它沒有任何區別,它只改變了分支的位置,這隻對'n = 0'很重要。 –

3

第一:這是純粹的猜測。我沒有寫這個代碼,也不能驗證我的猜測。

有代碼的這兩個版本之間的一個非常重要的語義差別:

// Version A 
if (*p++ == (unsigned char)c) 
    return ((void *)(p - 1)); 

// Version B 
if (*p == (unsigned char)c) 
    return ((void *) p); 
++p; 

在版本A中if的代碼塊之前的增量測序,而版本B的是後測序塊。

因此,在版本A中的增量代碼將其很可能從if生成的分支指令之前。至少我們可以將國際海事組織從編寫代碼時(1988年?)的編譯器假設這種從C代碼到彙編的相對直接翻譯。

是否與條件內聯後增有一些編譯器/平臺的一些 優化中受益?

有增量的分支允許相對簡單的優化架構上,其分支指令具有前delay slot:您可以將增量成延遲槽代替具有NOP那裏。

所以版本A需要每循環迭代一個指令小於版本B,在一個單一的遞減函數返回時的成本。這是一個(微)優化。

相關問題