一次效能提升300%的優化實踐
IntPtr handle = font.ToHfont(); //效能瓶頸
//…
SafeNativeMethods.DeleteObject(handle);
由於該控制元件在使用GDI畫字時,通過呼叫Font.ToHfont()方法獲得Font的Handle。而這個方法非常慢。並且控制元件在畫每個Item時都被呼叫這個方法,Form中又有很多個這樣的控制元件,因此呼叫次數相當可觀。這就造成了這個效能瓶頸。
由於作業系統是不允許GDI 的Handle個數大於9999的。如果大於9999個的話,程式就會崩掉。因此,我們絕對不能使程式中GDI的Handle個數與某些因素有線性增長關係。所有,一般都是在使用GDI畫字時建立Handle,用完之後就刪除掉。這樣也可以防止GDI洩露。
考慮到很多時候,Font都是相同的,如果能將Font建立的Handle快取起來,效能就會有很大的提升。但是,快取的Handle不及時刪除的話,如果Font不相同的太多,就有機會達到作業系統允許的最大個數,從而使程式崩潰。
以下是我的解決方案:
1,使用SafeFontHandle類來防止GDI洩露。SafeFontHandle派生自SafeHandleZeroOrMinusOneIsInvalid,而SafeHandleZeroOrMinusOneIsInvalid又派生自CriticalFinalizerObject。GC會對CriticalFinalizerObject做特別處理,保證所有關鍵終止程式碼都有機會執行。
#region The SafeFontHandle class
internal sealed class SafeFontHandle : SafeHandleZeroOrMinusOneIsInvalid
{
private SafeFontHandle()
: base(true)
{
}
public SafeFontHandle(IntPtr preexistingHandle, bool ownsHandle)
: base(ownsHandle)
{
base.SetHandle(preexistingHandle);
}
protected override bool ReleaseHandle()
{
return SafeNativeMethods.DeleteNativeFontHandle(base.handle);
}
}
#endregion
2,使用HandleCollector類防止Font的Handle超過作業系統最大限制。HandleCollector會跟蹤Font的Handle,並在其達到指定閥值時強制執行垃圾回收。垃圾回收後,SafeFontHandle會釋放Font的handle。
[SuppressUnmanagedCodeSecurity]
internal static class SafeNativeMethods
{
private static HandleCollector FontHandleCollector = new HandleCollector("GdiFontHandle", 500, 1000);
internal static IntPtr CreateNativeFontHandle(Font font)
{
IntPtr handle = font.ToHfont();
if (handle != IntPtr.Zero)
{
FontHandleCollector.Add();
}
return handle;
}
internal static bool DeleteNativeFontHandle(IntPtr handle)
{
bool success = DeleteObject(handle);
if (success)
{
FontHandleCollector.Remove();
}
return success;
}
[System.Runtime.InteropServices.DllImportAttribute("gdi32.dll")]
internal static extern bool DeleteObject(System.IntPtr gdiObject);
}
3,使用弱引用快取類WeakReferenceCachePool
internal static class SafeFontHandleFactory
{
#region Instance Data
private static WeakReferenceCachePool<Font, SafeFontHandle> _cachePool = new WeakReferenceCachePool<Font, SafeFontHandle>();
#endregion
#region Methods
public static SafeFontHandle CreateSafeFontHandle(Font font)
{
if (font == null)
{
throw new ArgumentNullException();
}
SafeFontHandle safeFontHandle = _cachePool[font];
if (safeFontHandle == null)
{
IntPtr nativeHandle = SafeNativeMethods.CreateNativeFontHandle(font);
safeFontHandle = new SafeFontHandle(nativeHandle, true);
_cachePool[font] = safeFontHandle;
}
return safeFontHandle;
}
#endregion
}
這樣就成功的快取了GDI的Handle,而且在使用完成後,GDI的Handle不會線性增長,只要有GC回收發生,GDI的Handle都會清零,或者總個數達到HandleCollector指定的閥值時,也會清零。利用GC垃圾回收機制,在效能和記憶體佔用之間自動平衡。
這裡是測試程式碼,效能測試如下:
不使用弱引用快取
Time Elapsed: 350ms
CPU Cycles: 952,061,115
Gen 0: 1
Gen 1: 0
Gen 2: 0
GDI increment: 0
使用弱引用快取
Time Elapsed: 42ms
CPU Cycles: 142,020,499
Gen 0: 0
Gen 1: 0
Gen 2: 0
GDI increment: 0
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/12639172/viewspace-611891/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 【效能優化實踐】優化打包策略提升頁面載入速度優化
- 記一次提升18倍的效能優化優化
- 記一次介面效能優化實踐總結:優化介面效能的八個建議優化
- gprof的效能優化實踐優化
- 關於 es 資料同步的一次效能優化實踐優化
- Canvas 動畫的效能優化實踐Canvas動畫優化
- 效能優化,實踐淺談優化
- SAP ABAP 效能優化實踐優化
- WEB站點效能優化實踐(載入速度提升2s)Web優化
- 基於 PageSpeed 的效能優化實踐優化
- hadoop JOB的效能優化實踐Hadoop優化
- TiDB 效能分析&效能調優&優化實踐大全TiDB優化
- ⚠️Flutter 效能優化實踐 總結⚠️Flutter優化
- FlutterWeb效能優化探索與實踐FlutterWeb優化
- 前端效能優化原理與實踐前端優化
- Mongodb特定場景效能數十倍提升優化實踐(記一次mongodb核心叢集雪崩故障)MongoDB優化
- 前端感官效能的衡量和優化實踐前端優化
- Tree-Shaking效能優化實踐 - 實踐篇優化
- 一次效能壓測及分析調優實踐
- 讀小程式效能優優化實踐-筆記優化筆記
- Vue 專案效能優化 — 實踐指南Vue優化
- HBase最佳實踐-讀效能優化策略優化
- 小程式效能優化的幾點實踐技巧優化
- 關於效能優化的一些實踐優化
- 記一次 VUE 專案優化實踐Vue優化
- 實踐指南-前端效能提升 270%前端
- Web效能優化系列:10個JavaScript效能提升的技巧Web優化JavaScript
- ASP.NET Core 效能優化最佳實踐ASP.NET優化
- OPPO萬億級資料庫MongoDB叢集效能數十倍提升優化實踐資料庫MongoDB優化
- 從一次效能優化看https的效能優化HTTP
- 系統效能提升優先法寶|快取應用實踐快取
- TiDB 效能分析&效能調優&最佳化實踐大全TiDB
- 一次 Flutter WebView 效能優化FlutterWebView優化
- ? 記一次前端效能優化前端優化
- 記一次前端效能優化的案例前端優化
- MySQL 效能優化:效能提升 50%,延遲降低 60%MySql優化
- 京東微信購物首頁效能優化實踐優化
- 2022 前端效能優化最佳實踐前端優化