[英]Why are standard string functions faster than my custom string functions?
我決定找到2個功能的速度:
這是我的xstrcmp函數:
int xstrlen(char *str)
{
int i;
for(i=0;;i++)
{
if(str[i]=='\0')
break;
}
return i;
}
int xstrcmp(char *str1, char *str2)
{
int i, k;
if(xstrlen(str1)!=xstrlen(str2))
return -1;
k=xstrlen(str1)-1;
for(i=0;i<=k;i++)
{
if(str1[i]!=str2[i])
return -1;
}
return 0;
}
我不想依賴strlen,因為我想要用戶定義的所有內容。
所以,我找到了結果。 strcmp每毫秒進行364次比較,而我的xstrcmp每毫秒只做20次比較(至少在我的電腦上!)
任何人都可以告訴為什么會這樣嗎? xstrcmp函數如何使自己如此之快?
if(xstrlen(str1)!=xstrlen(str2)) //computing length of str1
return -1;
k=xstrlen(str1)-1; //computing length of str1 AGAIN!
你正在計算str1
TWICE的長度。 這就是你的功能失去游戲的一個原因。
此外,與(大多數)標准庫中定義的相比, xstrcmp
的實現非常幼稚。 例如,你的xstrcmp
比較一個字節,實際上它可以一次比較多個字節,同時利用正確的對齊,或者在實際比較之前可以進行少量預處理以對齊存儲塊。
strcmp和其他庫例程由經驗豐富的工程師用匯編或專用C代碼編寫,並使用各種技術。
例如,程序集實現可能一次將四個字節加載到寄存器中,並將該寄存器(作為32位整數)與另一個字符串中的四個字節進行比較。 在某些機器上,程序集實現可能會加載8個字節甚至更多。 如果比較顯示字節相等,則實現繼續到接下來的四個字節。 如果比較顯示字節不相等,則實現停止。
即使進行這種簡單的優化,也有許多問題需要解決。 如果字符串地址不是四個字節的倍數,則處理器可能沒有加載四個字節的指令(許多處理器需要四個字節的加載才能使用與四個字節的倍數對齊的地址)。 根據處理器的不同,實現可能必須使用較慢的未對齊加載或為每個對齊情況編寫特殊代碼,這些對齊情況會對齊加載並移位寄存器中的字節以對齊要比較的字節。
當實現一次加載四個字節時,它必須確保它不會加載超出終止空字符的字節,如果這些字節可能導致段錯誤(錯誤,因為你試圖加載一個不可讀的地址)。
如果四個字節確實包含終止空字符,則實現必須檢測它並且不繼續比較其他字節,即使當前四個字符在兩個字符串中相等。
其中許多問題需要詳細的匯編指令,並且C中沒有對所使用的確切指令的所需控制。所使用的確切技術從處理器模型到處理器模型各不相同,並且在架構之間存在很大差異。
更快地實現strlen:
//Return difference in addresses - 1 as we don't count null terminator in strlen.
int xstrlen(char *str)
{
char* ptr = str;
while (*str++);
return str - ptr - 1;
}
//Pretty nifty strcmp from here:
//http://vijayinterviewquestions.blogspot.com/2007/07/implement-strcmpstr1-str2-function.html
int mystrcmp(const char *s1, const char *s2)
{
while (*s1==*s2)
{
if(*s1=='\0')
return(0);
++s1;
++s2;
}
return(*s1-*s2);
}
如果我有空的話,我會稍后再做另一個。 您還應該注意,大多數這些都是使用匯編語言或使用其他優化方法完成的,這些方法比您可以編寫的最佳直接C實現更快。
嘗試這個:
int xstrlen(const char* s){
const char* s0 = s;
while(*s) s++;
return(s - s0);
}
int xstrcmp(const char* a, const char* b){
while(*a && *a==*b){a++; b++;}
return *a - *b;
}
這可能會通過一些循環展開來加速。
1.算法
您的strcmp實現可能有更好的算法。 根本不需要調用strlen,每次調用strlen都會再次遍歷字符串的整個長度。 你可以在網上找到簡單但有效的實現,可能的起點就像:
// Adapted from http://vijayinterviewquestions.blogspot.co.uk
int xstrcmp(const char *s1, const char *s2)
{
for (;*s1==*s2;++s1,++s2)
{
if(*s1=='\0') return(0);
}
return(*s1-*s2);
}
這並不能解決所有問題,但在大多數情況下應該很簡單。
2.編譯器優化
這是一個愚蠢的問題,但請確保在編譯時打開所有優化開關。
3.更復雜的優化
編寫庫的人通常會使用更高級的技術,例如一次加載一個4字節或8字節的int,並進行比較,只有在整個匹配時才比較單個字節。 您需要成為專家才能知道這種情況的適用性,但您可以找到人們討論堆棧溢出最有效的實現(鏈接?)
如果編碼器可以知道編譯器可以找到比編譯器更高效的實現,則某些平台的某些標准庫函數可以手工編寫。 這種情況現在越來越少見,但在某些嵌入式系統中可能很常見。
4.鏈接器“欺騙”標准庫
使用一些標准庫函數,鏈接器可能能夠讓您的程序調用它們比調用代碼中的函數更少,因為它旨在了解更多關於函數的特定內部(鏈接?)我不知道是否在這種情況下適用,它可能不適用,但這是你必須考慮的事情。
好的,好的,我明白了,但什么時候我應該實現自己的strcmp?
在我的頭頂,這樣做的唯一原因是:
但是......
好的,你為什么要避免依賴strlen? 你擔心代碼大小嗎? 關於代碼或可執行文件的可移植性?
如果有充分的理由,請打開另一個問題,可能會有更具體的答案。 所以,如果我遺漏了一些明顯的東西,我很抱歉,但依靠標准庫通常要好得多,除非你想要改進一些特定的東西。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.