none
使用 for 迴圈重複 new 非靜態類別 class 的執行時間不同 RRS feed

  • 問題

  • 大家好:

    我最近要用C#主控台應用程式寫許多演算法,並比較它們的執行時間,但發現下面問題:

    假設我寫了兩個 非靜態 的演算法類別 A, B。

    System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();

    for ( int i = 0; i < 3 ; i++ )
    {
    A  a1 = new A();
    A  a2 = new A();
    B  b  = new B();

    sw.Reset();
    sw.Start();
    a1.Run();
    sw.Stop();
    Console.WriteLine(sw.Elapsed.TotalMilliseconds / 1000);

    sw.Reset();
    sw.Start();
    a2.Run();
    sw.Stop();
    Console.WriteLine(sw.Elapsed.TotalMilliseconds / 1000);

    sw.Reset();
    sw.Start();
    b.Run();
    sw.Stop();
    Console.WriteLine(sw.Elapsed.TotalMilliseconds / 1000);
    }


    在 i = 0時, a2 會跑得比 a1 明顯地快。

    在 i = 0時, a1, a2, b 都會比 i = 1, 2 時的 a1, b 都明顯地慢,為什麼呢?  如何解決呢?

    2015年8月31日 上午 10:55

解答

  • 這應該是 JIT 造成的原因吧.

    一個 Method (不論是 Static 或是 Instance Method), 他第一次被執行的時候會需要從 IL Code 編譯成真正可執行的 Native Code

    所以 i=0 時 a2 會比 a1 快, 因為第一次執行 a1.Run() 時,他必須先編譯 Run()  Method. 再來等到 a2.Run() 時, CLR 會發現 Run() Method 已經被編譯成了 Native Code. 所以就直接執行, 不再編譯.

    另外有些其他的干擾也會造成執行時間不一致, 因為你的環境裡並不是只有這隻程式在跑,光 Windows  一啟動就一堆執行緒在跑了, 除非你的 CPU 核心可以多到比環境內已經在執行的執行緒還多, 否則難免就是會遇到類似分時多工切換的狀況.

    關於 JIT 的問題, 是可以採用 NGen 事先編譯來減少這情況的發生, 不過NGen 的眉角也很多, 請自行參考文件.

    而且, 我的疑惑是 : 每次執行時間都一樣有這麼重要嗎 ?


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。



    2015年8月31日 上午 11:25
    版主

所有回覆

  • 這應該是 JIT 造成的原因吧.

    一個 Method (不論是 Static 或是 Instance Method), 他第一次被執行的時候會需要從 IL Code 編譯成真正可執行的 Native Code

    所以 i=0 時 a2 會比 a1 快, 因為第一次執行 a1.Run() 時,他必須先編譯 Run()  Method. 再來等到 a2.Run() 時, CLR 會發現 Run() Method 已經被編譯成了 Native Code. 所以就直接執行, 不再編譯.

    另外有些其他的干擾也會造成執行時間不一致, 因為你的環境裡並不是只有這隻程式在跑,光 Windows  一啟動就一堆執行緒在跑了, 除非你的 CPU 核心可以多到比環境內已經在執行的執行緒還多, 否則難免就是會遇到類似分時多工切換的狀況.

    關於 JIT 的問題, 是可以採用 NGen 事先編譯來減少這情況的發生, 不過NGen 的眉角也很多, 請自行參考文件.

    而且, 我的疑惑是 : 每次執行時間都一樣有這麼重要嗎 ?


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。



    2015年8月31日 上午 11:25
    版主
  • 感謝 Bill Chung 的回答,原來是這個原因造成,又學到了新東西。

    您的疑惑 : 每次執行時間都一樣有這麼重要嗎 ?

    為了避免po上來的code會誤導主要問題,有將不必要的code拿掉。

    事實上Run( ) 方法有傳遞演算法的初始的參數進去,然後比較演算法的快慢,光是 相同的 參數 a2 就跑得比 a1 快,才會讓比較的時間不準確, i = 0 時 a1, a2差了快 8ms,而每個演算法也才跑幾毫秒,所以影響很大。

    感謝。


    2015年8月31日 上午 11:42
  • 喔, 我突然恍然大悟, 因為你要比較每個演算法的速度是嘛 ?

    有一個方式可以做, 先執行各個方法 , 這時不需要算時間 , 等於讓所有的 Method 先經過JIT編譯.

    再來, 每個 Method 用個別的大迴圈 (上萬上十萬之類的) 去跑 (不要一個迴圈內跑不同的 Method). 用大數法則取平均值, 可能會比較能夠有統計上的意義.


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。




    2015年8月31日 上午 11:47
    版主
  • 由於 Windows 多工架構,且 CPU 執行程式碼快速,要比速度請用大範圍回圈除上迴圈數,再比 CPU Time ,不要比 Real Time 。

    不精確的問法,就會得到隨便猜的答案;自己都不肯花時間好好描述問題,又何必期望網友會認真回答?

    2015年9月4日 上午 09:39