2013-04-15 3 views
13

У меня есть некоторые странные результаты производительности, которые я не могу объяснить. кажется, что эта линиянизкая производительность многомерного массива initializer

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

в 4 раза медленнее, чем это один

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; 

(и это даже не принимая во внимание тот факт, что в этом примере я мог бы оставить все эти = 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

ответ

1

Вот хорошее объяснение инициализаторов массива и почему вы видите такие разные результаты : http://bartdesmet.net/blogs/bart/archive/2008/08/21/how-c-array-initializers-work.aspx

В принципе - инициализатор массива включает в себя создание настраиваемых структур, а прямое назначение каждого элемента - просто прямое назначение в стеке, и хотя оно быстрее.

Смежные вопросы