版權聲明:轉載時請以超鏈接形式標明文章原始出處和作者信息及本聲明
http://bigwhite.blogbus.com/logs/37947818.html
比較以下兩組代碼,你認爲哪組運行的更快些呢?
Example1:
int n = 100;
int n4 = n >> 2;
int i = 0;
int a[100];
for (i = 0; i < n4 ;i += 4) {
a[i] = i;
a[i+1] = i+1;
a[i+2] = i+2;
a[i+3] = i+3;
}
Example2:
for (i = 0;i < 100;i++) {
a[i] = i;
}
其實這個問題在"代碼大全2nd"中也有討論,從"代碼大全"中的統計結果來看,一般來說Example1更佔有優勢。我在solaris上做了測試,在未開優化的情況下:兩者運行時間分別爲2ms和6ms;在打開-O2優化後,兩者均爲1ms。這種通過減少循環次數的方法在GLIBC中也有體現,比如說strncpy的實現:
下面是strncpy的GLIBC源碼:
char *
x_strncpy (s1, s2, n)
char *s1;
const char *s2;
size_t n;
{
reg_char c;
char *s = s1;
--s1;
if (n >= 4)
{
size_t n4 = n >> 2; /* n4 = n / 4, n4表示下面的循環執行的次數*/
for (;;)
{
c = *s2++;
*++s1 = c;
if (c == '/0')
break;
c = *s2++;
*++s1 = c;
if (c == '/0')
break;
c = *s2++;
*++s1 = c;
if (c == '/0')
break;
c = *s2++;
*++s1 = c;
if (c == '/0')
break;
if (--n4 == 0)
goto last_chars; /* 如果n = 10,s2 = "hello world",則兩輪循環後,還有"尾巴"沒有copy完,在last_chars處繼續處理 */
}
n = n - (s1 - s) - 1; /* 還沒有copy完n個字節,s2就到達末尾了,跳到zero_fill處繼續爲s1補零 */
if (n == 0)
return s;
goto zero_fill;
}
last_chars:
n &= 3; /* n = n & 3 結果 n <= 3,n即爲上面循環過後"尾巴字符"的數量 */
if (n == 0)
return s;
do
{
c = *s2++;
*++s1 = c;
if (--n == 0)
return s;
} while (c != '/0');
zero_fill:
do
*++s1 = '/0';
while (--n > 0);
return s;
}
相比於strlen的實現,strncpy的實現更易理解。其字面上的邏輯就是每四個字節(n>>2)作爲一組,每組逐個字節進行拷貝賦值,其內在目的則是減少循環次數,以獲得性能的提升。要想知道爲什麼減少循環次數能提升性能的話,那就要深入到彙編層面去了,這裏不再詳述。另外還要一提的是GLIBC中的strncmp,strncat的實現也遵循着與上面同樣的邏輯。