其實很多寫.NET程式的開發人員都很喜歡通過一些計時器來看來一程式或代碼的運行效率,的確這樣是可以計算出代碼執行所損耗的時間。但.net程式的最佳化不僅僅在於此.大家知道.net提供自動記憶體回收機制,讓我們不用煩惱記憶體回收問題;同樣.net提供給我們的記憶體配置機制也很出色,因為它能非常快速地幫我們進行記憶體配置工作。當我們在享受吃糖的樂趣的時候,別忘了這東西吃多了很容易把牙齒給搞壞的;同樣.net 回收記憶體的時候同樣也讓難受,當然這些情況不會在你資源充足的時候給你帶來煩惱;不過一但出現他足可以讓你吃不下飯。
所以最佳化.net程式的時候不要忘了GC這東西,解決他的辦法只有一個就是分析那裡產生記憶體,想盡辦法去產生記憶體的地方給幹了(說得有點粗爆).前段時間瞭解了一個對象序列化組件,測試了一下效能發現其效率真的很出色。測試代碼如下:
int count = 100000; KJFObject kobj = new KJFObject(); kobj.Mm = new int[] { 34, 24, 545 }; kobj.Haha = "asfsfasfasfas"; kobj.Bind(); System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); sw.Start(); for (int i = 0; i < count; i++) { kobj = new KJFObject(); kobj.Mm = new int[] { 34, 24, 545 }; kobj.Haha = "asfsafsafsafasdfasfasf"; kobj.Bind(); } sw.Stop(); Console.WriteLine(sw.Elapsed.TotalMilliseconds); BufferWriter writer = new BufferWriter(Encoding.UTF8); sw.Reset(); sw.Start(); for (int i = 0; i < count; i++) { BObject bobj = new BObject(); bobj.Mm = new int[] { 34, 24, 545 }; bobj.Haha = "asfsafsafsafasdfasfasf"; writer.Reset(); bobj.Save(writer); } Console.WriteLine(sw.Elapsed.TotalMilliseconds);
他的序列化效率比直接代碼方式效率只低了50%,這50%的差距帶的效果就是編寫對象的時候更靈光和代碼更少,這是完全可取的。其實測試結果是偏向於後者,如果細心的朋友一定發現一個問題,BufferWriter是可複用的;如果沒有這種機制的情況那後者是完全輸給前者的。以下是兩個實體的定義
public class KJFObject: IntellectObject { private int[] _mm; [IntellectProperty(0)] public int[] Mm { get { return _mm; } set { _mm = value; } } private string _haha; [IntellectProperty(1)] public string Haha { get { return _haha; } set { _haha = value; } } } public class BObject:IMessage { private int[] _mm; public int[] Mm { get { return _mm; } set { _mm = value; } } private string _haha; public string Haha { get { return _haha; } set { _haha = value; } } public void Load(BufferReader reader) { Mm = reader.ReadInt32s(); Haha = reader.ReadString(); } public void Save(BufferWriter writer) { writer.Write(Mm); writer.Write(Haha); } }
如果緊緊是這樣效率上的差異,那必然不會選擇手寫代碼來做,因為手寫代碼不好維護,容易出錯特別是在大量成員讀寫順序上.從效率上來說這個組件可以說是完全勝任。當我在做一下步測試的時候發現組件的問題,就是沒有記憶體複用機制;於是用記憶體分析工具分析一下記憶體使用量狀況。發現其記憶體使用量情況有點差,記憶體的開銷有點大,在大並發下其GC壓力估計不少。以下是一個測結果:
從測試結果來看排在前頭的有Enumerator,這東西是怎產生的呢,其實就是我們習慣用Foreach產生的,從執行效率上來說Foreach和for沒多大差異,差別就在記憶體配置和回收上。當然你能通過用for把這個對象的開銷幹了,能就更好的事情.byte[] char[]這些都可以通過複用的給幹了,這樣大頭記憶體開銷基本就沒有了。
那手寫代碼控制序列化同樣的工作其記憶體使用量又怎樣呢
從記憶體使用量結果來看其差距還是非常大的,足足有5-6倍的差異,這些記憶體的GC回來在高並發下足可以讓整體處理效能最少有著10%的提升。所以記憶體複用在.net程式最佳化中起著十分重要的作用,只是這種最佳化能體現出來的情境有限;不過當你的產品面對運營的時候發現那就是件比較麻煩的事情,因為這些調整很有可能影響一些核心的地方。當你面對高效能應用的時候別忘了GC這個傢伙,它真是一個要命的東西。