2014-07-11 11 views
5

Operacja polega na pomnożeniu każdego i-tego elementu tablicy (nazwij go A) i i-tego elementu macierzy o tym samym rozmiarze (B) i aktualizować ten sam i-ty element A z uzyskaną wartością.Jak mogę zmaksymalizować wydajność operacji element-mądry na dużej tablicy w C#

W arytmetyczna wzorze A '[i] = A [b] x B [I] (0 < i < n (A))

Co najlepszy sposób zoptymalizować działanie w multi -reklama środowiska?

Oto mój obecny kod;

var learningRate = 0.001f; 
var m = 20000; 
var n = 40000; 
var W = float[m*n]; 
var C = float[m*n]; 

//my current code ...[1] 
Parallel.ForEach(Enumerable.Range(0, m), i => 
{ 
    for (int j = 0; j <= n - 1; j++) 
    { 
     W[i*n+j] *= C[i*n+j]; 
    } 
}); 

//This is somehow far slower than [1], but I don't know why ... [2] 
Parallel.ForEach(Enumerable.Range(0, n*m), i => 
{ 
    w[i] *= C[i] 
}); 


//This is faster than [2], but not as fast as [1] ... [3] 
for(int i = 0; i < m*n; i++) 
{ 
    w[i] *= C[i] 
} 

Przetestowano następujące metody. Ale występ wcale się nie poprawił. http://msdn.microsoft.com/en-us/library/dd560853.aspx

public static void Test1() 
    { 
     Random rnd = new Random(1); 

     var sw1 = new Stopwatch(); 
     var sw2 = new Stopwatch(); 
     sw1.Reset(); 
     sw2.Reset(); 

     int m = 10000; 
     int n = 20000; 
     int loops = 20; 

     var W = DummyDataUtils.CreateRandomMat1D(m, n); 
     var C = DummyDataUtils.CreateRandomMat1D(m, n); 

     for (int l = 0; l < loops; l++) 
     { 
      var v = DummyDataUtils.CreateRandomVector(n); 
      var b = DummyDataUtils.CreateRandomVector(m); 

      sw1.Start(); 

      Parallel.ForEach(Enumerable.Range(0, m), i => 
      { 
       for (int j = 0; j <= n - 1; j++) 
       { 
        W[i*n+j] *= C[i*n+j]; 
       } 
      }); 
      sw1.Stop(); 

      sw2.Start(); 
      // Partition the entire source array. 
      var rangePartitioner = Partitioner.Create(0, n*m); 

      // Loop over the partitions in parallel. 
      Parallel.ForEach(rangePartitioner, (range, loopState) => 
      { 
       // Loop over each range element without a delegate invocation. 
       for (int i = range.Item1; i < range.Item2; i++) 
       { 
        W[i] *= C[i]; 
       } 
      }); 

      sw2.Stop(); 

      Console.Write("o"); 
     } 

     var t1 = (double)sw1.ElapsedMilliseconds/loops; 
     var t2 = (double)sw2.ElapsedMilliseconds/loops; 

     Console.WriteLine("t1: " + t1); 
     Console.WriteLine("t2: " + t2); 
    } 

Wynik:

t1: 119

t2: 120,4

+1

Moje zrozumienie jest [1] jest najbardziej zoptymalizowane, ponieważ [2] tworzy zbyt wiele kolejek, co zwiększa koszty dodatkowego przetwarzania i alokowania zadań, aby uwolnić wątki zmniejszające wydajność, podczas gdy [3] działa na pojedynczy wątek, więc brak równoległości. Ale [1] najlepiej sprawdza się w obu przypadkach, tzn. Równolegle, aby wykorzystać wielordzeniowe/wątki i jeszcze niezbyt wiele kolejek do przetworzenia. –

+1

Optymalizacja mikro, jak rozwijanie pętli może pomóc tutaj. – leppie

+1

Uruchamianie i zatrzymywanie stopera w pętli nie będzie bardzo dokładne. – leppie

Odpowiedz

3

Problemem jest to, że podczas wywoływania delegata jest stosunkowo szybki, dodaje się po wywołaniu to wiele razy, a kod wewnątrz delegata jest bardzo prosty.

Zamiast tego można użyć parametru Partitioner, aby określić zakres, który ma być iterowany, co pozwala na iterację wielu elementów dla każdego wywołania delegata (podobnie jak w [1]):

Parallel.ForEach(Partitioner.Create(0, n * m), partition => 
    { 
     for (int i = partition.Item1; i < partition.Item2; i++) 
     { 
      W[i] *= C[i]; 
     } 
    }); 
Powiązane problemy