【问题标题】:C# Fast Element Find Matrix as MatlabC# 快速元素查找矩阵作为 Matlab
【发布时间】:2018-09-24 07:22:40
【问题描述】:

我有一个 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 倍。我怎样才能改善这个持续时间?

【问题讨论】:

  • 您在寻找 1 个元素吗?如果是这样,请在找到后返回,这样如果您已经拥有该元素,则不必遍历集合的其余部分。
  • 请问element_matrix在C#的情况下是什么类型?
  • @DmitryBychenko element_matrix 是 Emgu.CV.Matrix 中的矩阵(1000,1000)
  • 你想要找到所有元素还是只找到 1 个
  • 最快的方法是编写一个 C++ dll 并使用指针和线程。第二快的结果是预分配一个结构数组并使用不安全的指针计数和线程。还不断地调用像element_matrix.width 这样的东西(或任何带有 . 的东西)也会减慢它的速度。

标签: c# performance for-loop matrix emgucv


【解决方案1】:

你的并行东西不是线程安全的。

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

但是这应该比你拥有的更快:

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 获得如此多的性能提升;你会得到一些,但它会做更多的工作。

【讨论】:

    【解决方案2】:

    你有没有研究过“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 毫秒来获取出现或第一次出现的列表

    【讨论】:

    • 感谢您的关注。我的另一个选择是 Math.Net 库。我会试试这个。
    猜你喜欢
    • 2015-02-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-23
    • 2011-11-30
    • 2015-05-13
    相关资源
    最近更新 更多