2014-07-11 12 views
5

L'operazione consiste nel moltiplicare ogni elemento i-esimo di un array (chiamiamolo A) e l'elemento i-esimo di una matrice della stessa dimensione (B), e aggiorna lo stesso i-esimo elemento di A con il valore guadagnato.Come ottimizzare le prestazioni dell'operazione basata su elementi su un array di grandi dimensioni in C#

In una formula aritmetica, A '[i] = A [i] * B [i] (0 < i < n (A))

Qual è il modo migliore per ottimizzare questa operazione in un multi -core ambiente?

Ecco il mio codice corrente;

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] 
} 

testato il metodo seguente. Ma le prestazioni non sono migliorate affatto. 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); 
    } 

Risultato:

t1: 119

t2: 120,4

+1

la mia comprensione è [1] è la più ottimizzato, motivo è [2] crea troppe code che aggiunge l'overhead di elaborazione extra e il compito alotment a discussioni libere, riducendo le prestazioni, mentre [3] viene eseguito su un thread singolo quindi nessuna parallelizzazione. Ma [1] fa il meglio di entrambi, ovvero parallelizzare per sfruttare multi-core/thread e ancora non troppe code da elaborare. –

+1

L'ottimizzazione micro come lo srotolamento del loop potrebbe essere d'aiuto. – leppie

+1

L'avvio e l'arresto del cronometro nel loop non saranno molto accurati. – leppie

risposta

3

Il problema è che, mentre invoca un delegato è relativamente veloce, si aggiunge quando si richiama molte volte e il codice all'interno del delegato è molto semplice.

Cosa si potrebbe provare invece è quello di utilizzare un Partitioner per specificare l'intervallo che si desidera iterare, che permette di iterare molti articoli per ogni invocazione delegato (simile a quello che si sta facendo in [1]):

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