Rimuovere Interlocked.Add in Parallel.For?
-
26-10-2019 - |
Domanda
Ho qualche codice per fare alcune ricerche e contare il verificarsi utilizzando parallel.for:
//...initialize _table with int values...
int elements=60;
int[] outerCounter=new int[elements];
Parallel.For(1, 2000, i0=>
{
int[] counter=new int[elements];
int nextPos0=_table[10+i0];
for(i1=i0+1; i1<1990; i1++){
//...here are also some additionale calculations done...
int nextPos1=_table[nextPos0+i1];
counter[nextPos1]++;
}
//synchronize
for(int i=0; i<elements;i++){
Interlocked.Add(ref outerCounter[i], counter[i]);
}
}
Questa versione è il modo più veloce quindi un calcolo sequenziale. Ma mi piacerebbe trovare una soluzione diversa per contare il verificarsi come l'Interocked.Add è un collo di bottiglia. Stavo indagando se PLINQ sarebbe un'opzione, ma non è stato finora in grado di trovare un modo per contare il verificarsi degli elementi nextPos1 in un array.
Soluzione
mi piacerebbe suggerire fondamentalmente la stessa cosa di Hans, ma ho pensato che sarebbe stato utile fornire qualche codice. Ecco come avrei probabilmente affrontare il problema:
//...initialize _table with int values...
int elements=60;
List<int[]> outerCounter=new List<int[]>();
Parallel.For(1, 2000, i0=>
{
int[] counter;
lock(outerCounter)
{
if (outerCounter.Count == 0)
counter = new int[elements];
else
{
counter = outerCounter[outerCounter.Count - 1];
outerCounter.RemoveAt(outerCounter.Count - 1);
}
}
int nextPos0=_table[10+i0];
for(i1=i0+1; i1<1990; i1++){
//...here are also some additionale calculations done...
int nextPos1=_table[nextPos0+i1];
counter[nextPos1]++;
}
lock (outerCounter)
outerCounter.Add(counter);
});
int totalCounter = new int[elements];
Parallel.For(0, elements - 1, i =>
{
foreach (int[] counter in outerCounter)
totalCounter[i] += counter[i];
});
Altri suggerimenti
Da quello che mi da codice che non si sta per essere in grado di farlo correttamente senza bloccare outcounter [i] dal momento che tutte le discussioni stanno andando a scrivere a tutti i valori in outcounter.
po 'tardi per la festa qui, ma se si sta incrementando solo i valori in contatore [] e outerCounter [], è possibile utilizzare una versione di overload di Parallel.For ()
Invece di creare una matrice locale di elementi di ogni ciclo, è possibile creare un locale alla esecuzione (e sarà gestito solo su di un filo per volta)
Ad esempio:
int elements=60;
int[] outerCounter=new int[elements];
Parallel.For (1, 2000,
() => new int[elements], // Initialize the local value.
(i0, state, counter) =>
{
int nextPos0=_table[10+i0];
for(i1=i0+1; i1<1990; i1++)
{
//...here are also some additionale calculations done...
int nextPos1=_table[nextPos0+i1];
counter[nextPos1]++;
}
}
counter => // Add the local value
{
for(int i=0; i<elements;i++)
{
Interlocked.Add(ref outerCounter[i], counter[i]);
}
}
);
Non ho testato il codice di cui sopra, ma questo è il jist di esso. Esso contribuirà a ridurre drasticamente la quantità di volte che si sta chiamando Interlocked.Add ()
Per ulteriori informazioni, questo sito è molto buona: http://www.albahari.com/threading/part5.aspx