2011-09-08 79 views
3

我正在閱讀glibc2.9的源代碼。閱讀strcpy函數的源代碼,性能不如我預期的那麼好。爲什麼glibc中strcpy的性能更差?

以下是strcpyglibc2.9的源代碼:

char * strcpy (char *dest, const char* src) 
    { 
     reg_char c; 
     char *__unbounded s = (char *__unbounded) CHECK_BOUNDS_LOW (src); 
     const ptrdiff_t off = CHECK_BOUNDS_LOW (dest) - s - 1; 
     size_t n; 

     do { 
      c = *s++; 
      s[off] = c; 
     } 
     while (c != '\0'); 

     n = s - src; 
     (void) CHECK_BOUNDS_HIGH (src + n); 
     (void) CHECK_BOUNDS_HIGH (dest + n); 

     return dest; 
    } 

因爲我不知道使用偏移的原因,我通過比較下面的代碼上面的代碼做了一些性能測試:

char* my_strcpy(char *dest, const char *src) 
{ 
    char *d = dest; 
    register char c; 

    do { 
     c = *src++; 
     *d++ = c; 
    } while ('\0' != c); 

    return dest; 
} 

結果的strcpy表現在我的測試是雪上加霜。我已經刪除了關於綁定指針的代碼。

爲什麼glibc版本使用偏移?

以下是關於測試的介紹。

  1. 平臺:86(英特爾(R)奔騰(R)4),gcc版本4.4.2
  2. 編譯標誌:沒有標誌,因爲我不希望任何優化;該命令是gcc test.c

我所使用的測試代碼如下:

#include <stdio.h> 
#include <stdlib.h> 

char* my_strcpy1(char *dest, const char *src) 
{ 
    char *d = dest; 
    register char c; 

    do { 
     c = *src++; 
     *d++ = c; 
    } while ('\0' != c); 

    return dest; 
} 

/* Copy SRC to DEST. */ 
char * 
my_strcpy2 (dest, src) 
    char *dest; 
    const char *src; 
{ 
    register char c; 
    char * s = (char *)src; 
    const int off = dest - s - 1; 

    do 
    { 
     c = *s++; 
     s[off] = c; 
    } 
    while (c != '\0'); 

    return dest; 
} 

int main() 
{ 
    const char str1[] = "test1"; 
    const char str2[] = "test2"; 
    char buf[100]; 

    int i; 
    for (i = 0; i < 10000000; ++i) { 
     my_strcpy1(buf, str1); 
     my_strcpy1(buf, str2); 
    } 

    return 0; 
} 

當使用my_strcpy1功能,輸出是:

[[email protected] test]#time ./a.out 

real 0m0.519s 
user 0m0.517s 
sys  0m0.001s 
[[email protected] test]#time ./a.out 

real 0m0.520s 
user 0m0.520s 
sys  0m0.001s 
[[email protected] test]#time ./a.out 

real 0m0.519s 
user 0m0.516s 
sys  0m0.002s 

當期運用my_strcpy2,輸出爲:

[[email protected] test]#time ./a.out 

real 0m0.647s 
user 0m0.647s 
sys  0m0.000s 
[[email protected] test]#time ./a.out 

real 0m0.642s 
user 0m0.638s 
sys  0m0.001s 
[[email protected] test]#time ./a.out 

real 0m0.639s 
user 0m0.638s 
sys  0m0.002s 

我知道了用命令time不是很準確。但我可以從用戶時間獲得答案。

更新:

To remove the cost used to calculate the offset, I removed some code and added a global variable. 

#include <stdio.h> 
#include <stdlib.h> 

char* my_strcpy1(char *dest, const char *src) 
{ 
    char *d = dest; 
    register char c; 

    do { 
     c = *src++; 
     *d++ = c; 
    } while ('\0' != c); 

    return dest; 
} 


int off; 

/* Copy SRC to DEST. */ 
char * 
my_strcpy2 (dest, src) 
    char *dest; 
    const char *src; 
{ 
    register char c; 
    char * s = (char *)src; 

    do 
    { 
     c = *s++; 
     s[off] = c; 
    } 
    while (c != '\0'); 

    return dest; 
} 

int main() 
{ 
    const char str1[] = "test1test1test1test1test1test1test1test1"; 
    char buf[100]; 

    off = buf-str1-1; 

    int i; 
    for (i = 0; i < 10000000; ++i) { 
     my_strcpy2(buf, str1); 
    } 

    return 0; 
} 

my_strcpy2的業績仍好於my_strcpy1更糟。然後我檢查了彙編的代碼,但沒有得到答案。

我也放大串的大小和my_strcpy1的業績仍好於my_strcpy2

+1

護理來發表您的平臺,編譯器版本,優化標誌的細節和你有兩種功能得到了實際時間? – NPE

+2

這是'strcpy'的C版本,你的平臺幾乎肯定有一個glibc使用的彙編版本。 –

+0

「...我做了一些性能測試......」。你做了什麼測試?你確定你測試了代碼的優化版本?所有這些'CHECK_BOUNDS_HIGH'宏看起來像是用於調試代碼版本的額外安全檢查(當啓用「有界指針」支持時)。使用這些調試宏來測試性能是沒有意義的。 – AnT

回答

1

基於我所看到的,我並不感到驚訝,你的代碼更快。

看看循環,你的循環和glibc循環幾乎完全相同。但glibc的前後都有一個額外的代碼...

一般來說,簡單的補償不會降低性能,因爲x86允許一個相當複雜的間接尋址方案。所以這裏的兩個循環可能會以相同的速度運行。

編輯:這是我的更新與你給的增加的信息。

您的字符串大小隻有5個字符。儘管偏移方法「可能」從長遠來看稍微快一點,但是在開始循環之前需要幾次操作才能計算偏移量的事實會減慢短字符串的速度。也許如果你嘗試更大的琴絃,那麼這個差距就會縮小,甚至可能完全消失。

+0

感謝您的更新。我也考慮過昨天用來計算抵消的成本。然後我刪除了抵消的計算。添加一個全局變量offset,它是在main的循環外部計算的。但my_strcpy2的性能仍然比my_strcpy1差。 – linuxer

+0

然後在這一點上,這很可能是由於CPU管道。也許你的舊Pentium 4不能有效地處理間接尋址。直到後期的核心2時代,我才進入HPC,所以我對奔騰產品一無所知。 (我在奔騰時代還在讀高中......) – Mysticial

+0

我拆解了my_strcpy1和my_strcpy2,它們兩者的循環的彙編代碼非常相似。我沒有找到任何有價值的信息。 – linuxer

7

它使用偏移方法,因爲這消除了循環中的一個增量 - glibc代碼只需增加s,而您的代碼必須增加sd

請注意,您正在查看的代碼是與體系結構無關的回退實現 - glibc已爲許多體系結構(例如,the x86-64 strcpy())重寫了程序集實現。

+2

更好的是,現代編譯器將這種函數視爲內置函數,所以可能幾乎從不使用後備代碼。 –

+1

我得到你,但我想研究獨立於體系結構的代碼。 – linuxer

+0

我想到昨天用來計算抵消的成本。我移動了用於計算偏移量的代碼,並添加了一個新的全局變量。但結果沒有改變。我在帖子中添加了最新的測試代碼。 – linuxer

相關問題