在clr var c#一書中,作者描述當用sealed修飾類時,可以提高系統性能而且建議大家也養成用sealed來修飾類的習慣。由於對性能二字比較敏感,所以本文先測試一下用sealed分別修飾和不修飾類時,new1億次的時間,然后說一下我的看法,如果有更好的測試方法,歡迎大家在此交流。
首先上我的測試代碼:
class Program { static void Main(string[] args) { for (int j = 0; j < 10; j++) { V t = new T(); V u = new U(); t.oo(); u.oo(); DateTime startTimeT = DateTime.Now; for (int i = 0; i < 100000000; i++) { t.oo(); } DateTime endTimeT = DateTime.Now; TimeSpan spanT = endTimeT - startTimeT; Console.WriteLine("not sealed class:" + spanT.Milliseconds.ToString()); DateTime startTimeU = DateTime.Now; for (int i = 0; i < 100000000; i++) { u.oo(); } DateTime endTimeU = DateTime.Now; TimeSpan spanU = endTimeU - startTimeU; Console.WriteLine("sealed class:" + spanU.Milliseconds.ToString()); Console.WriteLine(); } Console.ReadKey(); } } class T : V { public override string oo() { return "Hello Another"; } } sealed class U:V { public override string oo() { return "Hello Another"; } } class V { public virtual string oo() { return "Hello Another"; } }
運行結果:
結果讓我大吃一驚,用sealed修飾的類除了第一次快了2毫秒外,剩下九次結果都慢大約五六毫秒,為什么修飾完以后效率反而下降了呢。當用sealed修飾類以后, 1.有助於JIT內聯;2.消除了協變與逆變和后期綁定,使CLR直接執行這個實例。
先說說“第1點”,促使JIT內聯代碼的因素有很多,JIT不會因為一個類是sealed,就去裝入其中的內容,而對於sealed類內部方法的內聯,很大原因也是由於方法槽映射關系決定的,sealed作用有待考證。
第2點,由於sealed類不可派生或被繼承,所以的確在運行時省去可CLR一些額外的工作,但是這些工作只是一些類似於“尋址”的工作,因為虛擬方法表已經完成了運行時與編譯時的對應關系,純粹的運行時只是在尋找這些關系而已,所以sealed省去的只是一部分較為復雜的尋址關系,因為即使沒有繼承,也不可避免一個方法表的應用。(上述兩點參考http://kb.cnblogs.com/page/73598/)
之所以CLR via C#一書中會這么寫,完全是因為作者本人的編程習慣。Jeffrey Richter不喜歡過分開放的對象,能private的絕對不public,除非確定要作為基類,不然一定會用sealed封死。
寫到這里,我又忍不住多測了幾遍,發現有時候修飾候后慢50多毫秒(460 vs 410),結果證明不但沒快反而慢了好多,希望有知道原因的大牛指點一下^.^