前陣子幫同事追查問題,由於懷疑主機上的程式版本有誤,便找來Source Code,與Reflector反編譯(Decompile)主機上DLL得到的Code比較,在一段程式上發現了小小的差異: Souce Code裡是先將DropDownList的SelectedValue先存到變數中,再將該變數當成呼叫另一個函數的參數;而Reflector中看到的卻是直接將DropDownList.SelectedValue直接被當成呼叫另一函數的參數。

程式寫法的差異,讓人懷疑上線的程式版本有誤。但同事印象中從頭到尾不曾用過這種寫法,追查上線記錄則顯示版本管理出錯的機率極低,到底是怎麼一回事? 幾經波折,最後在其他地方找到錯誤的根源,與程式邏輯無關,證實了程式版本並沒有任何問題。但引發一個有趣的結論: "DLL反編譯的結果與Source Code可能存在差異",之前在探討Nullable Value Type時,曾有類似的經驗,莫非這次又遇到?

一些效能討論文章中提過Compiler本身會做一些最佳化,剛才提到Source Code將DropDownList.SelectedValue存成變數,變數只被用了一次--當成呼叫函數時的參數。少了這個變數不會對邏輯有任何影響,還可以省下宣告/建立變數的成本,的確是種最佳化。

為了印證這點,我寫了以下的程式,還順道驗證一下上回提過的靜態字串相加會直接被Compiler做掉的理論,程式如下:

using System;
using System.Collections.Generic;
using System.Text;
 
namespace SmartCompiler
{
    class Program
    {
        static void Main(string[] args)
        {
            int j = 0;
            string s = "Hello " + "World!";
            string t = s;
            Console.Write(t);
            int i = 3 + 5;
            Console.Write(i);
        }
    }
}

用Reflector解析一下,有幾個地方被"最佳化"了:

1.變數j宣告了卻從未用到,Compiler直接忽略,在DLL中完全未現身。
2.由於s只被用來指定給t,所以就直接用t取代s。
3."Hello World!"直接變成一個字串指定給t。
4.Compiler直接算好3+5=8指定給i。

Compiler真是愈來愈聰明了,不過不代表以後程式可以亂寫,再交給Compiler去調。Compiler能做的小修正有限,省下來多半只是幾個Tick的極微時間,有時數百萬個Compiler最佳化也抵不上Coding時的一個小疏忽,只能當成錦上添花。

講到Coding時應注意效能,最近又再度見識到在上萬次迴圈中反覆做Database查詢的"好"程式,一整個囧!


Comments

# by Leo

謝謝分享.

# by Rex Tang

這讓我回想起前一陣子看到 Scott Hanselman 的文章: http://www.hanselman.com/blog/ReleaseISNOTDebug64bitOptimizationsAndCMethodInliningInReleaseBuildCallStacks.aspx compiler在最佳化 source code時是會抄捷徑的...

# by chicken

compiler 偷吃步的花招很多, 古早的 c compiler 就有看到...

# by elleryq

沒錯,常常有些錯誤只要關掉最佳化選項就消失了~ 這在 C/C++ 蠻常見的問題~ 所以常看到有人鼓勵 developer 盡可能地自己就先幫程式最佳化,不要讓編譯器幫你作這些功~

# by 小班

潛水多時,終於找到一個可以問您的問題。 請問 .net 可否自訂程式的最佳化?例如: for( int i=0; i<3; i++){ print(i); } 假設 .net 編譯器並不會針對以上程式做最佳化處理, .net framework 是否有語法讓開發人員是否可以自行將以上程式轉變成: print(0); print(1); print(2);

# by Jeffrey

to 小班,在我所知的.NET編譯最佳化中,倒沒看過你說的迴圈展開法。依我個人粗淺的想法是,這種展開在組合語言這類,幾個CPU Clock都要計較的低階語言裡,比較能突顯其效,.NET本身已是挺高階的語言,也許差別不大,所以沒看到相關的討論。 不過,我在這方面的所知所學蠻有限的,以上純屬個人的猜測。本站的讀者如果有熟稔這方面的高手牛人,歡迎出個聲,提供一些意見。

# by 小班

謝謝您的回覆,我的想法是,可否將一些複雜的語法整理成簡單的呼叫方式,在編輯時可以容易使用,編譯時則轉變回原來的複雜的語法。 我的重點是,將「簡單的呼叫方式」轉變成「複雜的語法」的動作是在編譯時處理,而不是每一 次執行時傻傻的再作一次轉換 ^^" 例如,一段自定格式頗複雜的文字,可以利用物件化的包裝,提供方法與參數來一小段一小段的 append 出整個字串,但最後產生的結果其實是 static 的字串!物件可以讓產生字串的工作變得更方便,但代價卻是執行時得做一些轉換,即使使用了 string builder ,還是會有其它一些羅輯運算的成本。

Post a comment