本文轉(zhuǎn)載自徐飛翔的"C語(yǔ)言中去除不必要的內(nèi)存引用可以有效地提高性能”版權(quán)聲明:本文為博主原創(chuàng)文章,遵循 CC 4.0 BY-SA 版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接和本聲明。
在C語(yǔ)言中,我們經(jīng)常會(huì)存在對(duì)某個(gè)內(nèi)存地址進(jìn)行引用的操作,也就是如下列代碼所示的,對(duì)指針進(jìn)行取內(nèi)容:
int vars[10];
int var = *(vars+3); //這里就是對(duì)數(shù)組vars的第三個(gè)元素進(jìn)行引用
這種內(nèi)存引用操作對(duì)應(yīng)的匯編代碼通常如:
mov (%rax), %rdx;
# 把地址位于%rax的內(nèi)存值進(jìn)行取出。
mov 12(%rax), %rdx;
# 把地址位于%rax+12的內(nèi)存值進(jìn)行取出。
我們注意到這種操作涉及到了CPU從數(shù)據(jù)總線中向內(nèi)存中取值,通常速度遠(yuǎn)遠(yuǎn)慢于CPU本身的計(jì)算操作,也慢于CPU取出內(nèi)部寄存器值的操作,很多時(shí)候,一個(gè)程序的計(jì)算瓶頸就在這些去內(nèi)存的操作中,因此要盡量避免不必要的內(nèi)存引用。以下舉個(gè)代碼例子進(jìn)行進(jìn)一步說(shuō)明。
// code_1.c
#include <stdio.h>
void foo(float vars[], int length, float *sum){
int i = 0;
for (i = 0; i < length; i++){
*sum = *sum * vars[i];
}
}
int main(){
float sum = 1;
float vars[] = {0.3,0.4,0.13,0.65,0.23,0.87,0.2,1.34};
int cycle = 0;
for (cycle = 0; cycle < 100000000; cycle++){
foo(vars, 8, &sum);
sum = 1;
}
return 0;
}
// code_2.c
#include <stdio.h>
void foo(float vars[], int length, float *sum){
int i = 0;
int tmp = *sum;
for (i = 0; i < length; i++){
tmp = tmp * vars[i];
}
*sum = tmp;
}
int main(){
float sum = 1;
float vars[] = {0.3,0.4,0.13,0.65,0.23,0.87,0.2,1.34};
int cycle = 0;
for (cycle = 0; cycle < 100000000; cycle++){
foo(vars, 8, &sum);
sum = 1;
}
return 0;
}
code_1.c
和code_2.c
的差別很小,就是在于函數(shù)foo()
中關(guān)于sum
這個(gè)指針的指向的內(nèi)容的更新方式,第一種方式是每一個(gè)循環(huán)中都進(jìn)行更新,顯然其需要更多但是卻沒(méi)必要的內(nèi)存引用,第二種通過(guò)一個(gè)臨時(shí)變量的形式,避免了多次頻繁無(wú)用地訪問(wèn)內(nèi)存。觀察其兩者的匯編,就會(huì)發(fā)現(xiàn)和我們之前分析的是一致的。我們采用-O1
優(yōu)化選項(xiàng),命令如:gcc -O1 -S code_1.c
gcc -O1 -S code_2.c
匯編結(jié)果如下所示(以下匯編只是截取部分關(guān)鍵信息)
# code_1.s
.L3:
movss (%rdx), %xmm0
mulss (%rax), %xmm0
movss %xmm0, (%rdx)
addq %4, %rax
cmpq %rcx, %rax
jne .L3
而第二個(gè)則簡(jiǎn)單很多
# code_1.s
.L3:
mulss (%rax), %xmm0
addq %4, %rax
cmpq %rcx, %rax
jne .L3
我們發(fā)現(xiàn),第一個(gè)代碼比起第二個(gè)代碼多出很多內(nèi)存引用操作,其需要從內(nèi)存中取出乘數(shù)
movss (%rdx), %xmm0
,計(jì)算完之后,有需要更新,將其寫(xiě)回內(nèi)存, movss %xmm0, (%rdx)
。導(dǎo)致其性能遜于后者。在筆者的服務(wù)器上,兩者的性能具體對(duì)比為:code 1跑了0.54s,而code 2跑了0.37s。同時(shí)我們發(fā)現(xiàn),編譯器很難對(duì)此進(jìn)行優(yōu)化,在
-O1
優(yōu)化等級(jí)下,其表現(xiàn)和我們分析的并沒(méi)有區(qū)別(某些編譯器優(yōu)化會(huì)導(dǎo)致代碼分析和實(shí)際的匯編有所區(qū)別),其還沒(méi)有能夠智能到對(duì)這種進(jìn)行優(yōu)化,因此需要程序員對(duì)此進(jìn)行顯式地優(yōu)化。