C#提高稀疏矩阵行和列总计算的并行性能

时间:2015-06-24 19:46:43

标签: c# task-parallel-library sparse-matrix

我有一个包含大约1亿个非零元素的稀疏矩阵:

// [Row][Column][Element]
public IDictionary<int, IDictionary<int, decimal>> MyMatrix { get; private set; }

获取每行的总和非常快:

private void RowSum()
{
    var rowTotals = new ConcurrentDictionary<int, decimal>();

    Parallel.ForEach(MyMatrix, (row) =>
    {
         rowTotals.TryAdd(row.Key, row.Value.Sum(x => x.Value));
    });
}

获取每列的总和要慢得多:

private void ColumnSum()
{
   var columnTotals = new ConcurrentDictionary<int, decimal>();

   Parallel.ForEach(MyMatrix, (row) =>
   {
        foreach (var column in row.Value)
        {
            columnTotals.AddOrUpdate(column.Key, column.Value, 
                 (key, old) => old + column.Value);
        }
   });
}

为了更快地进行列计算,我可以创建一个[Column] [Row] [Element]矩阵,但这会使RAM需求翻倍。是否有任何方法或数据结构可以使列计算与行计算一样快,而不会使ram加倍?

3 个答案:

答案 0 :(得分:3)

可能发生的事情是集中ConcurrentDictionary存在争议。如果是这种情况,您可以尝试localInit的{​​{1}}重载,为每个任务批处理提供自己的本地(和无竞争)Parallel.ForEach,然后将其聚合到中心字典中结束:

Dictionary

修改

一些时间(100000 x 100000空间中的10M填充元素)

  • 你的RowSum 425ms
  • 您的ColumnSum 7774ms
  • localInit ColumnSum 3324ms

所以仍然比行总和慢一个数量级,但看起来是一个合理的改进。

(我的词典使用中也有错误)

答案 1 :(得分:1)

我认为

 Parallel.ForEach(MyMatrix, (row) =>
   {
        foreach (var column in row.Value)
        {
            columnTotals.AddOrUpdate(column.Key, 0, (key, old) => old + column.Value);
        }
   });

应该是

 Parallel.ForEach(MyMatrix, (row) =>
   {
        foreach (var column in row.Value)
        {
            columnTotals.AddOrUpdate(column.Key, column.value, (key, old) => old + column.Value);
        }
   });

我认为您可以通过public IDictionary<Tuple<int, int>, decimal> MyMatrix { get; private set; }

开始,使效果更加对称(但不会更快

答案 2 :(得分:0)

如果最高和最低列值之间的差异足以创建一个简单的int数组,您可以按以下步骤操作:

确定最高和最低值,以进一步创建一个对应数组,该对应数组与每个列值相关联,用于对值进行求和的2个数组中的索引,即存储列值的数组,以及并行的列总计。

您的代码如下:

private void ColumnSum()
{
   int highestKeyValue=int.MinValue;
   int lowestKeyValue =int.MaxValue;
   Parallel.ForEach(MyMatrix, (row) =>
   { // identify highest and lowest column value
     foreach (var column in row.Value) 
     {
       lowest =Math.Min(lowestKeyValue ,column.Key) ; 
       highest=Math.Max(highestKeyValue,column.Key) ; 
      }
   // Create correspondence array 
   int[] corrrespondence=new int[highestKeyValue-lowestKeyValue]; 
   for (int i=0;i<highest-lowest;i++) corrrespondence[i]=-1 ; 
   Parallel.ForEach(MyMatrix, (row) =>
   { // tag the key values found in matrix
     foreach (var column in row.Value) corrrespondence[column.Key-lowest]=0 ; 
   }
   int columnsCount=0 ;
   // compute the indexes to result arrays
   for (int i=0;i<highest-lowest;i++) 
     if (corrrespondence[i]>=0) corrrespondence[i]=columnsCount++  ; 
   // allocate and initialize results array
   int[] columnValues=new int[columnsCount]() ;
   int[] columnTotals=new int[columnsCount]() ;
   for (int i=0;i<columnsCount;i++) columnTotals[i]=0 ; 
   Parallel.ForEach(MyMatrix, (row) =>
   {
     foreach (var column in row.Value)
     {
       int j=correspondence[column.Key-lowest] ;
       // a lock on results[j] is required there to avoid concurrent update of results[j] 
       columnValues[j]=column.Key ;
       columnTotals[j]+=column.Value ;
     }
   }
 }