2013-04-15 28 views
13

我有一些奇怪的性能結果,我無法完全解釋。 看來,這個線多維陣列初始化器的性能下降

d = new double[4, 4]{{1, 0, 0, 0}, 
        {0, 1, 0, 0}, 
        {0, 0, 1, 0}, 
        {0, 0, 0, 1},}; 

比這個

d = new double[4, 4]; 
d[0, 0] = 1; d[0, 1] = 0; d[0, 2] = 0; d[0, 3] = 0; 
d[1, 0] = 0; d[1, 1] = 1; d[1, 2] = 0; d[1, 3] = 0; 
d[2, 0] = 0; d[2, 1] = 0; d[2, 2] = 1; d[2, 3] = 0; 
d[3, 0] = 0; d[3, 1] = 0; d[3, 2] = 0; d[3, 3] = 1; 

慢4倍(這甚至沒有考慮到一個事實,在這個例子中,我可以離開了所有那些= 0分配)

我知道在c#中的多維數組上循環會因邊界檢查而變慢。但是這裏沒有循環,不需要邊界檢查,整個數組初始化行可以在編譯時解析。

然而,第二個代碼塊必須首先將數組初始化爲零,然後分別覆蓋每個值。
那麼這裏有什麼問題?

如果性能是一個問題,那麼初始化這個數組的最佳方法是什麼?


我用下面的代碼來衡量性能:

using System; 
using System.Diagnostics; 
class Program 
{ 
    public static double[,] d; // global static variable to prevent the JIT optimizing it away 

    static void Main(string[] args) 
    { 
     Stopwatch watch; 
     int numIter = 10000000; // repeat all tests this often 

     double[,] d2 = new double[4, 4]{{1, 0, 0, 0}, 
             {0, 1, 0, 0}, 
             {0, 0, 1, 0}, 
             {0, 0, 0, 1},}; 

     // ================================================================ 
     // use arrayInitializer: slowest 
     watch = Stopwatch.StartNew(); 
     for (int i = 0; i < numIter; i++) 
     { 
      d = new double[4, 4]{{1, 0, 0, 0}, 
           {0, 1, 0, 0}, 
           {0, 0, 1, 0}, 
           {0, 0, 0, 1},}; 
     } 
     Console.WriteLine("ArrayInitializer: \t{0:0.##########}ms", watch.ElapsedMilliseconds * 1.0/numIter); 

     // ================================================================ 
     // use Array.Copy: faster 
     watch = Stopwatch.StartNew(); 
     for (int i = 0; i < numIter; i++) 
     { 
      d = new double[4, 4]; 
      Array.Copy(d2, d, d2.Length); 
     } 
     Console.WriteLine("new + Array.Copy: \t{0:0.##########}ms", watch.ElapsedMilliseconds * 1.0/numIter); 

     // ================================================================ 
     // direct assignment: fastest 
     watch = Stopwatch.StartNew(); 
     for (int i = 0; i < numIter; i++) 
     { 
      d = new double[4, 4]; 
      d[0, 0] = 1; d[0, 1] = 0; d[0, 2] = 0; d[0, 3] = 0; 
      d[1, 0] = 0; d[1, 1] = 1; d[1, 2] = 0; d[1, 3] = 0; 
      d[2, 0] = 0; d[2, 1] = 0; d[2, 2] = 1; d[2, 3] = 0; 
      d[3, 0] = 0; d[3, 1] = 0; d[3, 2] = 0; d[3, 3] = 1; 
     } 
     Console.WriteLine("direct assignment: \t{0:0.##########}ms", watch.ElapsedMilliseconds * 1.0/numIter); 
    } 
} 

結果:

ArrayInitializer:  0,0007917ms 
new + Array.Copy:  0,0002739ms 
direct assignment:  0,0002281ms 
+0

看看編譯的IL代碼是非常不同的。 ArrayInitializer使用一種方法RuntimeHelpers.InitializeArray。但那是我能做的最好的...有趣的問題! – Aron

+0

您從不使用創建的數組,所以整個數組分配是否會被編譯器優化掉? – Servy

+0

這就是爲什麼我使數組公共靜態。如果它只是一個局部變量,那麼它確實被優化了,但只適用於數組初始值設定項的第一個測試用例。但是如果'd'是一個靜態變量,則不應該有這樣的優化,因爲另一個線程可以想象得到它;時間測試似乎證實了這一點。 – HugoRune

回答