初級優化篇
說到優化,很多人又不屑一顧了,“現在計算機速度都那么快了,再快那么百分之幾有什么意義啊”。這么說確實有些道理,現在的編譯器編譯后的結果已經是充分優化過了,除了圖形圖像多媒體等特定軟件的開發外、多數情況下刻意的優化確實沒必要,但是如果開發人員在編寫代碼的時候已經具有了優化意識,在完成優化的同時,又能保證了甚至提升開發效率,何樂而不為呢?
當然,算法的設計都是優化的核心,絕大多數情況下,程序的執行效率高低主要由開發人員對程序整體把握,算法的設計等來決定!但有時候針對細節的優化也是有一定意義的!
而且這種優化在很多情況下也并不需要直接通過匯編來寫代碼實現,但這種情況下卻也能體現出掌握匯編知識的優越性!
如下面兩個函數:
function GetBit(i: Cardinal; n: Cardinal): Boolean;
begin
Result := Boolean((i shr n) and 1);
end;
function GetBit(i: Cardinal; n: Cardinal): Boolean;
begin
Result := Boolean((1 shl n) and i);
end;
對應的匯編代碼:
MOV ECX, EDX
SHR EAX, CL
AND EAX, $01
MOV ECX, EDX
MOV EDX, $01
SHL EDX, CL
AND EAX, EDX
它們的作用一樣,都是取i某位的值,為1返回True,0返回False!
表面上看可能都會認為兩個函數的執行效率一樣,實際上還是有區別的,第一段程序是的移位操作是對i進行的,按照Delphi中默認的調用約定register,此時的i的值是存在寄存器EAX中,移位操作可直接完成;而第二段程序則不同,要對立即數1完成移位操作,必須先將其傳送到寄存器,由此也就必然多出一條指令!當然也不是所有情況下,指令少就一定比指令多要快,具體執行時還要考慮指令執行的時鐘周期和指令的配對等問題(后面再介紹些),獨立出來也說明不了問題,只有在具體代碼環境中才好作比較。
一般情況下這種效率上的執行差異實在是太微不足道了,但在編程期間時刻保持著優化的意識絕不是件壞事!如果此類代碼位于循環的最里層,N個時鐘周期經過大量循環的累積,產生的執行效率差異也可能變的很大!
上面只是個很小的例子,由此可以看出在開發中如果能站在匯編的角度思考一些問題,能在保證開發效率的同時用高級語言編寫出更有效率的細節代碼!但還有很多時候,細節優化還要用使用嵌入匯編代碼來完成,而且有些時候由于嵌入匯編代碼應用,還能使代碼編寫變得更有效率。
如需要將一個32位數的字節順序顛倒,在Delphi中,完全用高級語言實現怎么做?用移位可以,多次調用內建函數Swap也可以,但是如果想到一條BSWAP指令,這一切變得很簡單。
function SwapLong(Value: Cardinal): Cardinal;
asm
BSWAP EAX
end;
注:同上,Value的值是存在寄存器EAX中,而32位數的值也通過EAX返回,所以只需要一句即可。
當然多數的嵌入匯編優化沒有這么簡單,不過通過大學里所學的那一點點匯編知識也很難做到更深入的優化,也只能通過不斷的積累,對比編譯后的匯編代碼獲取經驗!好在多數情況下,細節優化并不是程序設計的主體。
但如果所開發程序涉及到圖形圖像多媒體等方面,還是有必要進行更深入的優化的!好在不管是浮點指令的優化還是應用MMX、SSE、3DNow等完成優化,Delphi6都能提供良好的支持。即使是想早期版本的Delphi支持這些CPU擴展指令集或者想要支持以后新的CPU指令集,利用Delphi在嵌入匯編中所支持的DB、DW、DD、DQ等四條匯編指令(在Borland的Delphi6官方語言手冊里只說支持DB、DW、DD)插入相關指令的數值表示也能靈活的實現。
如:
DW $A20F //CPUID
DW $770F //EMMS
DB $0F, $6F, $C1 //MOVQ MM0, MM1
了解指令只是基礎,在圍繞FPU,MMX,SSE設計完算法后,想更深一步的進行優化,還必須了解一些CPU本身的技術特性。
先看看下面兩段代碼:
asm
ADD [a], ECX
ADD [b], EDX
end
asm
MOV EAX, [a]
MOV EBX, [b]
ADD EAX, ECX
ADD EBX, EDX
MOV [a], EAX
MOV [b], EBX
end
第二個效率高?錯了,如上面說的,指令少不意味著執行效率高,查查相關資料可知,第一段代碼的兩條指令執行的時鐘周期為3(每條指令都需要完成讀、改、寫三步),第二段代碼中的6條指令執行的時鐘周期都為1。那么說兩段代碼效率一樣?又錯了,實際上第二段代碼執行效率比第一段代碼要高!為什么?因為奔騰級以后的CPU都有兩條流水線來執行指令,所以當相鄰的兩條指令能夠完成配對,那么它們就能夠同時執行!具體到上面的兩段代碼來說具體原因又是什么呢?
第一段代碼中的兩條指令雖然可以完成配對,但需要的總執行時鐘周期為5而不是3,而第二段代碼的六條指令可以兩兩之間并行執行,所以也就導致了這個結果。
說到這里,都是些很淺顯的例子,本身給不了大家太多的幫助。如果真的想優化特定程序,還是找些FPU,MMX優化的專題文章看看,或者找來技術手冊好好專研專研“亂序執行”和“分枝預測”等技術。只希望各位在上大學的朋友們不要只專注于那些“能賺錢”的開發工具和時髦的新技術,能把更多的時間花在打基礎上,有了扎實的基礎才能快速掌握新知識、才能用更快的時間掌握新的開發工具、才能...(省略一千字)。
不過話又說回來,知識還是要用來解決實際問題的,如果每天就只在技術細節上做文章,也許能成為一個出色的黑客,但絕對開發不出一流的軟件。所以還是要以創造價值為根本目的。所以...不說了,再說下去就真不像技術文章了。^_^
附:程序優化除了考慮執行效率以外,當然也要考慮體積的問題(體積小才能更快的載入內存,更快的完成指令譯碼等工作),比如清空EAX寄存器都是用SUB EAX, EAX或XOR EAX, EAX而不會用MOV EAX, $0,雖然它們的執行時鐘周期都是1,但前者的指令長度(2字節)明顯比后者(5字節)短。但因為上面說的都是些細節,所以沒提到體積的問題。更多的縮小體積的問題還是交給編譯器去解決吧,在編寫嵌入ASM代碼的同時稍微注意一下就可以了。
新聞熱點
疑難解答
圖片精選