【问题标题】:Optimizing this code block优化此代码块
【发布时间】:2012-05-16 12:19:50
【问题描述】:
for (int i = 0; i < 5000; i++)
   for (int j = 0; j < 5000; j++)
   {
      for (int ii = 0; ii < 20; ii++)
          for (int jj = 0; jj < 20; jj++)
           {
               int num = matBigger[i+ii][j+jj];
               // Extract range from this.
               int low = num & 0xff;
               int high = num >> 8;
               if (low < matSmaller[ii][jj] && matSmaller[ii][jj] > high)
                  // match found
           }
   }

机器是 x86_64,32kb L1 cahce,256 Kb L2 缓存。

关于如何优化此代码的任何指示?

编辑原始问题的一些背景:Fastest way to Find a m x n submatrix in M X N matrix

【问题讨论】:

  • 您可以尝试展开内部循环。
  • 我认为优化这段代码是不可能的,除非重新考虑整个算法以减少循环次数。这段代码应该做什么?
  • 选择不同的数据结构!
  • 代码试图在一个更大的矩阵中找到一个更小的矩阵。有一些限制,看我之前的问题stackoverflow.com/questions/10529278/…
  • 如果您尝试模糊template matching,如果您在频域中执行此操作或通过提前停止(或小波变换的比例/低通部分)。但这一切都取决于您的匹配标准和上下文。

标签: c++ optimization loops


【解决方案1】:

我要尝试的第一件事是将ii 和jj 循环移到i 和j 循环之外。这样,您就可以在 i 和 j 循环的 2500 万次迭代中使用 matSmaller 的相同元素,这意味着您(或者如果幸运的话,编译器)可以在这些循环之外提升对它们的访问:

for (int ii = 0; ii < 20; ii++)
  for (int jj = 0; jj < 20; jj++)
    int smaller = matSmaller[ii][jj];
    for (int i = 0; i < 5000; i++)
      for (int j = 0; j < 5000; j++) {
        int num = matBigger[i+ii][j+jj];
        int low = num & 0xff;
        if (low < smaller && smaller > (num >> 8)) {
          // match found
        }
      }

这可能会更快(由于对matSmaller 数组的访问较少),或者可能会更慢(因为我已经更改了对matBigger 数组的访问模式,并且可能我已经它对缓存不太友好)。类似的替代方法是将ii 循环移到i 和j 之外并提升matSmaller[ii],但将jj 循环留在内部。经验法则是,在内部循环中增加多维数组的 last 索引比之前的索引更便于缓存。所以我们修改jj和j比修改ii和i更“快乐”。

我要尝试的第二件事 - matBigger 的类型是什么?看起来其中的值只有 16 位,因此请尝试使用 int 和 (u)int16_t。前者可能更快,因为对齐的int 访问速度很快。后者可能会更快,因为任何时候都有更多的数组适合缓存。

您可以通过对smaller 的一些早期分析来考虑一些更高级别的事情:例如,如果它是0,那么您不需要检查matBigger 的值ii 和jj,因为num &amp; 0xff &lt; 0 总是假的。

要做得比“猜测并查看它们是否更快”做得更好,您首先需要知道哪条线路最热门,这意味着您需要一个分析器。

【讨论】:

  • 非常感谢史蒂夫的回答。:)。
【解决方案2】:

一些基本建议:

  1. 对其进行分析,以便您了解热点在哪里。
  2. 考虑缓存局部性,以及循环顺序产生的地址。
  3. 在最内层范围内使用更多const,向编译器提示更多信息。
  4. 尝试分解它,这样如果 low 测试失败,您就不会计算 high。
  5. 尝试显式地保持到matBigger 和matSmaller 的偏移量,直到最里面的步进到一个简单的增量。

【讨论】:

    【解决方案3】:

    最好理解代码应该做什么,然后检查是否存在其他算法来解决这个问题。

    除此之外:

    • 如果您只是对是否存在匹配条目感兴趣,请确保在 // match found 的位置跳出所有 3 个循环。
    • 确保以最佳方式存储数据。这完全取决于您的问题,但即只有一个大小为 5000*5000*20 的数组并重载 operator()(int,int,int) 以访问元素可能会更有效。

    【讨论】:

      【解决方案4】:

      matSmaller 和 matBigger 是什么? 尝试将它们更改为matBigger[i+ii * COL_COUNT + j+jj]

      【讨论】:

        【解决方案5】:

        我同意史蒂夫关于重新排列循环以将更多计数作为内部循环的观点。由于您的代码仅进行加载和比较,因此我相信很大一部分时间用于指针运算。尝试一个实验来改变史蒂夫的答案:

        for (int ii = 0; ii < 20; ii++)
          {
          for (int jj = 0; jj < 20; jj++)
            {
            int smaller = matSmaller[ii][jj];
            for (int i = 0; i < 5000; i++)
              {
              int *pI = &matBigger[i+ii][jj];
              for (int j = 0; j < 5000; j++)
                {
                int num = *pI++;
                int low = num & 0xff;
                if (low < smaller && smaller > (num >> 8)) {
                  // match found
                } // for j
              } // for i
            } // for jj
          } // for ii
        

        即使在 64 位模式下,C 编译器也不一定能很好地将所有内容保存在寄存器中。通过将数组访问更改为简单的指针增量,您将使编译器的工作更容易生成高效的代码。

        编辑:我刚刚注意到@unwind 提出了基本相同的建议。另一个需要考虑的问题是比较的统计数据。低比较或高比较更有可能吗?安排条件语句,使不太可能的测试在前。

        【讨论】:

          【解决方案6】:

          看起来这里有很多重复。一种优化是减少重复工作量。使用笔和纸,我将 matBigger"i" 索引迭代为:

          [0 + 0], [0 + 1], [0 + 2], ..., [0 + 19],
                   [1 + 0], [1 + 1], ..., [1 + 18], [1 + 19]
                            [2 + 0], ..., [2 + 17], [2 + 18], [2 + 19]
          

          如您所见,有些位置会被多次访问。 此外,乘以迭代计数表示访问内部内容:20 * 20 * 5000 * 5000,或 10000000000 (10E+9) 次。太多了!

          所以与其试图加快 10E9 指令的执行速度(如执行(管道)缓存或数据缓存优化),不如尝试减少迭代次数。

          代码正在矩阵中搜索一个范围内的数字:大于最小值且小于最大范围值。

          基于此,尝试不同的方法:

          1. 查找并记住搜索值较大的所有坐标 比低值。让我们称之为锚点。
          2. 对于每个锚点,找到后第一个值的坐标 范围之外的锚点。

          目标是减少重复访问的次数。锚点允许一次性扫描并允许其他决策,例如查找范围或确定包含锚值的 MxN 矩阵。

          另一个想法是创建包含matBigger 和matSmaller 的新数据结构,这些结构更适合搜索。

          例如,为matSmaller 中的每个唯一值创建一个{value, coordinate list} 条目:

            Value    coordinate list
              26 -> (2,3), (6,5), ..., (1007, 75)
              31 -> (4,7), (2634, 5), ...
          

          现在您可以使用此数据结构在matSmaller 中查找值并立即知道它们的位置。因此,您可以在 matBigger 中搜索此数据结构中的每个唯一值。这再次减少了对矩阵的访问次数。

          【讨论】:

            猜你喜欢
            • 2018-08-04
            • 2011-04-09
            • 1970-01-01
            • 2019-04-14
            • 1970-01-01
            • 2011-11-24
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多