Удалить Interlocked.Add в Parallel.For? - PullRequest
0 голосов
/ 16 марта 2011

У меня есть некоторый код, чтобы выполнить поиск и подсчитать вхождение, используя 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]);
  }
}

Эта версия намного быстрее, чем последовательный расчет.Но я хотел бы найти другое решение для подсчета вхождения как Interocked. Add является узким местом.Я изучал, будет ли Plinq вариантом, но пока не смог найти способ подсчета вхождения элементов nextPos1 в массиве.

Ответы [ 3 ]

0 голосов
/ 16 марта 2011

Я бы предложил то же самое, что и Ганс, но я подумал, что было бы полезно предоставить некоторый код.Вот как я бы, вероятно, решил проблему:

//...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];
});
0 голосов
/ 23 марта 2011

Здесь немного поздно, но если вы только увеличиваете значения в counter [] и outerCounter [], вы можете использовать перегруженную версию Parallel.For ()
Вместо создания локального массива элементов каждого цикла, вы можете создать один локальный для выполнения (и будет работать только один поток за раз) Например:

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

Я не тестировал приведенный выше код, но в этом суть. Это значительно сократит количество вызовов, которые вы вызываете Interlocked.Add ()

Для получения дополнительной информации, этот сайт очень хорош: http://www.albahari.com/threading/part5.aspx

0 голосов
/ 16 марта 2011

Исходя из того, что я получаю из кода, вы не сможете сделать это правильно без блокировки outcounter [i], поскольку все потоки будут записывать все значения в outcounter.

...