C#快速元素查找矩阵作为Matlab

时间:2018-09-24 07:22:41

标签: c# performance for-loop matrix emgucv

我有一个1000x1000的矩阵,并且使用Emgu CV。 我尝试在此矩阵中找到元素索引

所以我首先在Matlab中尝试

test_matrix=rand(1000,1000);
tic
[row,col]=find(test_matrix==test_matrix(1,1));
toc;
  

它在9.7毫秒内完成。

然后我在C#中使用经典的for循环。

for (int i = 0; i < element_matrix.Height; i++)
 for (int j = 0; j < element_matrix.Width; j++)
    if (element_matrix[i, j] == finding_element)
       {
         Find_Row_List.Add(i);
         Find_Col_List.Add(j);         
       }
  

它在460毫秒内完成。

然后我将矩阵划分为10个小矩阵,并计算不同线程中的每个部分。

             t1= new Thread(() => {
                for(int i = 0; i < 100; i++)
                {
                    for(int j=0;j<element_matrix.Width;j++)
                    {
                        if(element_matrix[i,j]==finding_element)
                        {
                            Find_Row_List.Add(i);
                            Find_Col_List.Add(j);
                        }
                    }
                }
            });
            ...
            t1.Start();
            t2.Start();
            ...
            t10.Start();

            t1.Join();
            t2.Join();
            ...
            t10.Join();
  

它在310毫秒内完成。

我对 20个小型矩阵和线程重复此过程。

  

它在380毫秒内完成。

然后我使用Parallel.For

  Parallel.For(0, element_matrix.Height, i =>
    {
        for(int j = 0; j < element_matrix.Width; j++)
        {
            if(element_matrix[i,j]==finding_element)
            {
                Find_Row_List.Add(i);
                Find_Col_List.Add(j);
            }
        }
    });
  

它在224毫秒内完成。

我使用两个线程和Parallel.For

      t1 = new Thread(() => {
            Parallel.For(0, 500, i =>
            {
                for (int j = 0; j < element_matrix.Width; j++)
                {
                    if (element_matrix[i, j] == finding_element)
                    {
                        Find_Row_List.Add(i);
                        Find_Col_List.Add(j);
                    }
                }
            });
        });

        t2 = new Thread(() => {
            Parallel.For(500, 1000, i =>
            {
                for (int j = 0; j < element_matrix.Width; j++)
                {
                    if (element_matrix[i, j] == finding_element)
                    {
                        Find_Row_List.Add(i);
                        Find_Col_List.Add(j);
                    }
                }
            });
        });

        t1.Start();
        t2.Start();

        t1.Join();
        t2.Join();
  

它在240毫秒内完成。

摘要

**Method                       Duration (ms)**
------------------------       ------------
Matlab                         9.7
For Loop (Classic)             460
For Loop (10 threads)          310
For Loop (20 threads)          380
Parallel.For                   224
Parallel.For(2 threads)        250

所有持续时间均为10次计算的平均值。

我尝试了与Matlab一样快的不同方法来进行计算。最快的解决方案是Parallel.For(224 ms)。但是它比Matlab慢25倍。我怎样才能改善这个持续时间?

2 个答案:

答案 0 :(得分:2)

您的并行对象不是线程安全的,

最快的方法是使用非托管代码,指针和(可能)线程

但这应该比您拥有的速度快

var width = Input.GetLength(0);
var height = Input.GetLength(1);
var array = new Point[width * height];
var count = 0;

fixed (Point* r = array)
fixed (double* pInput = Input)
{
   var len = array.Length;

   for (var i = 0; i < len; i++)
      if (*(pInput + i) == someValue)
      {
         var temp = r + count++;
         (*(temp)).X = i;
         (*(temp)).Y = i / width;
      }

   var result = new Point[count];
   Array.Copy(array, 0, result, 0, count);
   return result;
}

基准

----------------------------------------------------------------------------
Mode             : Release (64Bit)
Test Framework   : .NET Framework 4.7.1 (CLR 4.0.30319.42000)
----------------------------------------------------------------------------
Operating System : Microsoft Windows 10 Pro
Version          : 10.0.17134
----------------------------------------------------------------------------
CPU Name         : Intel(R) Core(TM) i7-2600 CPU @ 3.40GHz
Description      : Intel64 Family 6 Model 42 Stepping 7
Cores (Threads)  : 4 (8)      : Architecture  : x64
Clock Speed      : 3401 MHz   : Bus Speed     : 100 MHz
L2Cache          : 1 MB       : L3Cache       : 8 MB
----------------------------------------------------------------------------

结果

--- Standard input ---------------------------------------------------------
| Value   |    Average |    Fastest |    Cycles | Garbage | Test |    Gain |
--- Scale 100 ----------------------------------------------- Time 0.163 ---
| Unsafe2 |  23.472 µs |  21.013 µs |  81.444 K | 0.000 B | N/A  | 80.92 % |
| Index   | 123.034 µs | 114.073 µs | 420.831 K | 0.000 B | Base |  0.00 % |
--- Scale 1,000 -------------------------------------------- Time 16.477 ---
| Unsafe2 |   2.940 ms |   2.324 ms |   9.761 M | 0.000 B | N/A  | 76.77 % |
| Index   |  12.657 ms |  12.021 ms |  43.033 M | 0.000 B | Base |  0.00 % |
----------------------------------------------------------------------------

您可以并行进行,虽然我不确定TPL会带来这么多的性能提升,尽管您会获得一些收益,但是工作量会更多

答案 1 :(得分:0)

您是否已进入“ Math.Net”库以进行多维操作。

        var sw = new Stopwatch();
        sw.Start();
        var test_matrix = Matrix<double>.Build.Dense(1000, 1000, 0);

        double finding_Element = 1;
        test_matrix[999, 999] = finding_Element;
        test_matrix[998, 999] = finding_Element;

        var result = new List<ValueTuple<int, int>>();

        for (int row = 0; row < 1000; row++)
        {
            for (int column = 0; column < 1000; column++)
            {
                if (test_matrix[row, column] == finding_Element)
                {
                    result.Add(new ValueTuple<int, int>(row, column));
                }
            }
        }

        sw.Stop();
        Console.WriteLine("Find List of Result: " + sw.ElapsedMilliseconds + "ms");

        var sw1 = new Stopwatch();
        sw1.Start();
        var result2 = test_matrix.Find(x => x.Equals(finding_Element)); // First Value
        sw1.Stop();
        Console.WriteLine("Find First Occurence: " + sw.ElapsedMilliseconds + "ms");

        Console.ReadLine();

我在这里的结果大约是34毫秒以获取出现或首次出现的列表