.NET 首次執行偏慢現象之 JIT 編譯效應觀察

栏目: ASP.NET · 发布时间: 5年前

内容简介:.NET 的 dll/exe 以也因為我原本以為 JIT 編譯在載入 Assembly 階段就完成了,故在分析首次執行偏慢原因時並未將 JIT 編譯因素納入考量,得知它方法為單位後,顯然是該重啟調查。

.NET 效能測試首次執行偏慢現象解析 一文得到老讀者 Lane Kuo 的迴響,補充一篇好文: .NET Just in Time Compilation and Warming up Your System by Abhinaba Basu ,一掃我對 .NET JIT 編譯的迷思。

.NET 的 dll/exe 以 CIL 通用中間語言 方式儲存程式碼,真正要執行前才將 CIL 進一步編譯成適用該主機硬體的機器程式碼(Machine Code),這個特性稱之為 JIT,Just In Time,編譯。但我一直以為 JIT 是發生在 Process 載入 Assembly 之際,看到 Abhinaba 用 windbg 證明「.NET 的 JIT 即時編譯是以方法為單位」才恍然大悟,.NET Runtime 會拖到真的要執行該方法前才執行 JIT 編譯,這策略基於成本效益,沒用到的東西就不花時間準備,合情合理。

也因為我原本以為 JIT 編譯在載入 Assembly 階段就完成了,故在分析首次執行偏慢原因時並未將 JIT 編譯因素納入考量,得知它方法為單位後,顯然是該重啟調查。

要克服 JIT 編譯延遲,有三種做法:

  • 使用 NGen 預先編譯
  • 強迫該方法預先進行 JIT 編譯 System.Runtime.CompilerServices.RuntimeHelpers.PrepareMethod()
  • 預先執行該方法暖身(就是前文使用的技巧)

本案例測試對象 MD5/SHA1 隸屬 System.Security.Cryptography 位於 mscorlib.dll。

.NET 首次執行偏慢現象之 JIT 編譯效應觀察

mscorlib 為 .NET 核心組件,基於效能考量在安裝 .NET Framework 時應已做過 NGen。使用 Performance Monitor 觀察可發現測試程式實際存取 C:\Windows\assembly\NativeImages_v4.0.30319_32\mscorlib\ \mscorlib.ni.dll 可為佐證:

.NET 首次執行偏慢現象之 JIT 編譯效應觀察

由此推測,MD5/SHA1/SHA256 等應不致受 JIT 編譯拖累。dotTrace 工具有個 Timeline Profiling 功能可觀察 JIT 編譯時間,以前篇文章的 TestMD5_First(); TestMD5_Second(); 程式為對象,測量總執行時間為 12.4ms,其中 JIT 編譯時間只佔 1.4ms。

.NET 首次執行偏慢現象之 JIT 編譯效應觀察

展開時間軸,觀察到的 JIT 編譯發生在 clr.dll、ntdll.dll、wow64cpu.dll、Program.cs Main(),未包含 mscorlib.dll,符合 Performance Monitor 記錄存取的已編譯版(Native Image)只有 mscorlib.ni.dll,其餘 clr.dll、ntdll.dll、wow64cpu.dll 都是 CIL 版。

.NET 首次執行偏慢現象之 JIT 編譯效應觀察

實驗至此,應可排除 JIT 編譯造成本案例首次執行偏慢的假設。

不過我還是做了測試,呼叫 System.Runtime.CompilerServices.RuntimeHelpers.PrepareMethod() 先準備 InitializeConfigInfo() 再執行。

static void Main(string[] args)
{

    Test(() =>
    {
        MethodInfo mi =
            typeof(CryptoConfig).GetMethod("InitializeConfigInfo",
                BindingFlags.Static | BindingFlags.NonPublic);
        System.Runtime.CompilerServices.RuntimeHelpers.PrepareMethod(mi.MethodHandle);
    }, "InitializeConfigInfo JIT Compilation");
    Test(() =>
    {
        MethodInfo mi =
            typeof(CryptoConfig).GetMethod("InitializeConfigInfo",
                BindingFlags.Static | BindingFlags.NonPublic);
        mi.Invoke(null, null);
    }, "\nInitializeConfigInfo Exec");

    Console.WriteLine();
    var flag = args.Length > 0 ? args[0] : "1";
    switch (flag)
    {
        case "1":
            Test1();
            Test1();
            break;
        case "2":
            Test2();
            Test2();
            break;
        case "3":
            Test3();
            Test3();
            break;
    }
}

實測結果挺有趣,PrepareMethod() 耗時 0.5ms,InitializeConfigInfo() 執行時間則由上篇文章的 6ms 降到 5.5ms,減少的時間差不多就是 PrepareMethod() 耗費的時間。

.NET 首次執行偏慢現象之 JIT 編譯效應觀察

InitializeConfigInfo() 位於 mscorlib.dll,已是 JIT 編譯後版本,為什麼 PrepareMethod() 還是有加速效果? 這問題遠超過我對 .NET Runtime 的理解,只能依據 官方文件 推敲:

Compilers use the PrepareMethod(RuntimeMethodHandle) method to handle virtual calls that are made inside a constrained execution region (CER). At JIT compilation time, the common language runtime does not usually have enough information about the target of a virtual call. Therefore, the runtime does not initially prepare that segment of the call graph. If the code that is using the CER has enough knowledge to determine the target at any point in time before the CER is actually entered, it can call PrepareMethod(RuntimeMethodHandle) to perform the same runtime preparation normally done for a CER rooted at the method specified as an argument.

裡面提到 PrepareMethod 可供在 JIT 編譯階段讓 .NET Runtime 準備好虛擬呼叫對象的 Call Graph 以便在進入 CER 時應用。猜想其中除了 JIT 編譯外還包含其他準備工作,才讓正式呼叫時加快 0.5ms。但以上純屬門外漢瞎猜,留待各方高人補充指正。

歸納本次的研究心得:

  1. .NET JIT 編譯發生各方法呼叫前,而非我原以為的 Assembly 載入時
  2. NGen 或 RuntimeHelpers.PrepareMethod() 可強迫預先 JIT 編譯
  3. 本案例用到的 mscorlib.dll 已做過 NGen,配合實測觀察,JIT 編譯不是首次執行偏慢主因
  4. dotTrace Timeline Profiling 可用於觀察 JIT 編譯耗時比例
  5. 即使已經過 JIT 編譯,RuntimeHelpers.PrepareMethod() 仍有加速效果

最後補充,本案例因 mscorlib.dll 已經過 NGen 避開 JIT 編譯的效能影響,故不適合用來推論 JIT 編譯對實務程式的影響,不過現在對原理有了基本認識,也學會觀察 工具 以及克服方法,倒不難設計實驗觀察 JIT 編譯對效能的影響,就排入 TODO 清單吧。

The article demostrates how to use Perfromance Monitor to check if the assembly had been NGen, use dotTrace to observe JIT compilation time and test if PrepareMethod() can imporve performance.


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

看见未来

看见未来

余晨 / 浙江大学出版社 / 2015-4-15 / 59.00元

【内容简介】 这是互联网群星闪耀的时代,巨人们用最尖端的技术和自成体系的哲学改变着我们的生活,甚至影响了整个世界和人类的历史进程。在这个时代,没有人可以避开互联网的渗透。互联网早已不是简单的技术变革,人们正试图赋予其精神和内涵,以期互联网能更好地为人类所用。 本书中作者 面对面地采访了包括马克·扎克伯格、埃隆·马斯克、杨致远、凯文·凯利、克里斯·安德森、罗伯特·希勒、迈克尔·莫瑞茨、凯......一起来看看 《看见未来》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

MD5 加密
MD5 加密

MD5 加密工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具