這是Bee的舊文章,貼出來讓大家參考。
這是Bee見過最好的文章,來解釋指標效能及寫法的影響。
取自"Pointer on C"由Kenneth A. Reek所著。
簡體書為"C和指針",人民郵電出版社出版。
內容的一段我簡單說明。
範例是使用68000處理器
程式碼共用此段宣告
#define SIZE 50
int x[SIZE];
int y[SIZE];
int i;
int *p1, *p2;
第一個程式
void try1()
{
for( i =0; i< SIZE; i++ )
x[i] = y[i];
}
很直覺的寫法。
組合語言碼為
_try1: clrl _i
jra L20
L20001: movl _i,d0
asll #2,d0
movl #_y,a0
movl _i,d1
asll #2,d1
movl #_x,a1
movl a0@(0,d0:L),a1@(0,d1:L)
addql #1,_i
L20: moveq #50,d0
cmpl _i,d0
jgt L20001
第二個程式
void try2()
{
for( p1 = x, p2 = y; p1 - x < SIZE; )
*p1++ = *p2++;
}
改使用指標。
組合語言碼為
_try2: movl #_x,_p1
movl #_y,_p2
jra L25
L20003: movl _p2,a0
movl _p1,a1
movl a0@,a1@
addql #4,_p2
addql #4,_p1
L25: moveq #4,d0
movl d0,sp@-
movl _p1,d0
subl #_x,d0
movl d0,sp@-
jbsr ldiv
addql #8,sp
moveq #50,d1
cmpl d0,d1
jgt L20003
組合語言碼變長了,而且因為指標相減調整(除4)而去呼叫ldiv函式。增加許多執行時間。
第三個程式
void try3()
{
for( i = 0, p1 = x, p2 = y; i < SIZE; i++ )
*p1++ = *p2++;
}
計數改用變數,不讓指標調整。
組合語言碼為
_try3: clrl _i
movl #_x,_p1
movl #_y,_p2
jra L30
L20005: movl _p2,a0
movl _p1,a1
movl a0@,a1@
addql #4,_p2
addql #4,_p1
addql #1,_i
L30: moveq #50,d0
cmpl _i,d0
jgt L20005
似乎不錯。但還有更好。
第四個程式
void try4()
{
register int *p1, *p2;
register int i;
for( i = 0, p1 = x, p 2= y; i < SIZE; i++ )
*p1++ = *p2++;
}
使用暫存器變數宣告,將區域變數使用暫存器。
組合語言碼為
_try4: moveq #0,d7
movl #_x,a5
movl #_y,a4
jra L35
L20007: movl a4@+,a5@+
addql #1,d7
L35: moveq #50,d0
cmpl d7,d0
jgt L20007
如果夠了解編譯器及語言特性,就會找到更好的調整。
第五個程式
void try5()
{
register int *p1, *p2;
for( p1 = x, p2 = y; p1 < &x[SIZE]; )
*p1++ = *p2++;
}
移除計數。
組合語言碼為
_try5: movl #_x,a5
movl #_y,a4
jra L40
L20009: movl a4@+,a5@+
L40: cmpl #_x+200,a5
jcs L20009
最精簡。但C原始碼可讀性極差。
這個例子可以看出效能及可讀性之間的移轉。
也可以看出如何依組合語言碼而調整C程式碼。
到底是要好讀,還是效能,端看使用場合而定。
這樣的分析只對這樣的系統有效,不同時代的c compiler以及不同廠家提供的不同compiler,分析結果就又不同,尤其是更精密效能探討更沒有絕對結果,我指的是當程式以特殊手法設計,而必須干涉系統的編譯來設計程式時更甚,此時關鍵程式仍然只能靠直接使用組合語言才容易實現,現行與以前的所有c compiler都仍沒有我講的這種能力,也許將來的才可以。
回覆刪除根據我使用c設計的gforth系統,已有十幾年七大版的成品,就呈現這種現象,如果要逐年逐版逐個硬體分析後,更改其中核心指令,採用最佳指標使用方法才能完成設計,才能改善品質,或讓性能符合真正forth的要求,gforth的原設計人自己也不願意做這麼囉嗦的事情,看來c仍然有許多比組合語言高階後就辦不到的事情,不巧這樣的要求卻又都是絕對關鍵,對forth而言就非要不可。
但無論如何,這種分析仍然對單一系統有效,可以就地解決暫時性的問題,是單一系統分析時很好的範例。
丁陳漢蓀老師需要一個c的好手來寫eforth的核心碼,如果您熟,何不下功夫接手?
曾慶潭
[版主回覆02/04/2010 10:42:25]您說得沒錯。
不過我這篇是要給一般C的使用者看。因為現在會C/C++的人很多,但沒幾個人會調。
我看到有人寫了近十年的C/C++,指標仍是他不想碰的,並會以不符合物件的觀念,違反軟體工程原則加以否決。後來有另一人去調整程式碼,使用指標寫出來的程式直接快三倍。
至於Forth語言,我暫時沒時間,近幾年會很忙,學業及家庭都有事。這個私下寄e-mail再談。
感謝您的回應,我想這對許多人都很有幫助。
,指標仍是他不想碰的,並會以不符合物件的觀念,違反軟體工程原則加以否決。
回覆刪除我很想聽他的說法哩
這種講法我還第一次聽到哩
在C++ polymorphism 的實現下,電腦底層還是用指標阿...
[版主回覆04/29/2010 10:07:36]指標是許多初階工程師的痛。我看到的是面子和技術相比,有許多人還是面子重要,所以搬出一堆理由。
我也看過使用VB寫了三十年的老工程師,不管為何,就是不想前進到C語言的領域,其中指標搞不懂應是原因。
但是人各有所長,不碰指標只是代表他不願前進到高階程式設計的領域。
hi,我實作了這幾個副程式,發現裡面的code有部份應該是筆誤
回覆刪除tyr1() 和 tyr2() 應該是寫反了,應該是try1() 和try2() 我猜啦
在try4()裡 p = y; 應該是p2=y; 才對
不過我拿microchip C30模擬的結果,try4() 和 try5() 並沒有特別的優化,可能是register並沒有發揮出應該有的特性吧!我想請問的是,如何更能掌握指標和mcu的關系? 謝謝
[版主回覆11/21/2010 22:18:20]你真細心,我筆誤沒發現。已修正了。
在暫存器少的CPU上,try4和try5應是差不了太多。
怎麼我做出來的結果try3()的效果最差??
回覆刪除C30 V1.2 dspic30f5015
clk rom ram
try1() 1525 6021 248
try2() 1483 6033 248
try3() 1582 6045 248
try4() 1026 6012 248
try5() 926 6003 248
[版主回覆11/01/2012 11:15:56]和處理器的結構有關。和編譯器最佳化也有關。
我的例子是以68K處理器為目標。它在指標上的管理本來就比其他處理器好很多。
所以指標操作的程式上會有加速。
換處理器測的話,還是看輸出的組合語言才會準。
在不善長指標操作的處理器上面,try3會是最吃虧。
若是如此,也表示此處理器亦不善長於影像相關的工作。
嗯嗯,明白了,有前輩可以問的感覺真好 ^^
回覆刪除[版主回覆11/01/2012 13:29:08]南部也有不錯的社團可以問,只是對我來說太遠了。
http://mosut.org/