【问题标题】:Efficiently Building Summed Area Table有效地建立总面积表
【发布时间】:2013-02-21 23:33:22
【问题描述】:

我正在尝试构建一个总面积表,以供以后在自适应阈值例程中使用。由于此代码将用于时间关键型软件,因此我试图从中挤出尽可能多的周期。

为了提高性能,表格中的每个像素都是无符号整数。

当我附加我的分析器时,我表明我最大的性能瓶颈发生在执行 x-pass 时。

计算的简单数学表达式是:

sat_[y * width + x] = sat_[y * width + x - 1] + buff_[y * width + x]
where the running sum resets at every new y position.

在这种情况下,sat_ 是代表 SAT 的无符号整数的一维指针,buff_ 是 8 位无符号单色缓冲区。

我的实现如下所示:

uint *pSat = sat_;
char *pBuff = buff_;

for (size_t y = 0; y < height; ++y, pSat += width, pBuff += width)
{
    uint curr = 0;
    for (uint x = 0; x < width; x += 4)
    {
        pSat[x + 0] = curr += pBuff[x + 0];
        pSat[x + 1] = curr += pBuff[x + 1];
        pSat[x + 2] = curr += pBuff[x + 2];
        pSat[x + 3] = curr += pBuff[x + 3];
    }
}

循环是手动展开的,因为我的编译器 (VC11) 没有为我这样做。我遇到的问题是整个分段例程花费大量时间来运行该循环,我想知道是否有人对什么可以加快它有任何想法。我可以访问所有 SSE 的集合,以及用于运行该例程的任何机器的 AVX,所以如果那里有什么东西,那将非常有用。

此外,一旦我挤出最后一个周期,我就计划将其扩展到多核,但我希望在使模型更复杂之前让单线程计算尽可能紧凑。

【问题讨论】:

  • 您的代码没有实现您在第一个表达式中编写的内容...具体来说,您在每一行的开头重置curr,但这不是表达式所暗示的。
  • 你说得对,我在数学上玩得又快又松。如果您对如何重新编写 x 通行证的数学有建议,我就是游戏。算法的实现是正确的,但是没有我希望的那么快。
  • 好的。我问的原因是因为这个代码是否可以被矢量化/并行化是至关重要的。听起来你很幸运,因为每行的重置都会破坏依赖链。
  • 是的,感觉我应该能够对其进行矢量化,但我只是想不出正确的调用,因为我本质上希望 y0..3 成为矢量,但 sse gather 不是直到 AVX2 才实现
  • 如果使用相同的饱和内存带宽,并行将无济于事。

标签: c++ visual-c++ image-processing sse


【解决方案1】:

每一行都有一个依赖链;每个结果都取决于前一个结果。所以你不能在那个方向矢量化/并行化。

但是,听起来每一行都独立于其他所有行,因此您可以通过同时计算多行来进行矢量化/并行化。您需要转置数组,以允许向量指令访问内存中的相邻元素。*

但是,这会产生一个问题。从缓存的角度来看,沿着行走现在绝对是可怕的(每次迭代都是缓存未命中)。解决这个问题的方法是交换循环顺序。

但请注意,每个元素都被精确读取一次。而且您对每个元素的计算量很少。因此,在达到 100% CPU 使用率之前,您基本上会受到主内存带宽的限制。


* 这个限制可能会在 AVX2 中解除,我不确定...

【讨论】:

  • 很有趣,因此它可以以必须运行转置为代价进行矢量化。
  • 对于 SSE 实现,而不是转置整个图像,您可能要考虑使用 16x16 切片。您可以进行快速的 16x16 转置,该转置主要基于寄存器,然后迭代 16 个结果向量,累积 16 个部分和。
  • @PaulR 我试过了,这是一种有趣的方法。不幸的是,它最终平均每张测试图像慢了约 1 毫秒。感谢您的提示。
  • 好的 - 是使用基于 SSE 寄存器的快速转置吗?
  • 我使用的是“_MM_TRANSPOSE4_PS”宏和“_mm_castsi128_ps”,因为没有 4 个整数的版本。 MSDN 说演员阵容是无操作的。有没有你想过的不同的转置?
【解决方案2】:

从算法上讲,我认为您无法做任何事情来进一步优化它。尽管您在描述中没有使用术语 OLAP 多维数据集,但您基本上只是在构建一个 OLAP 多维数据集。您拥有的代码是构建 OLAP 多维数据集的标准方法。

如果您提供有关您正在使用的硬件的详细信息,可能会有一些可用的优化。例如,有一种 GPU 编程方法可能会更快,也可能不会。注意:该线程上的另一篇文章提到并行化是不可能的。这不一定是真的...您的算法不能并行实现,但有些算法可以保持数据级并行性,可以通过 GPU 方法加以利用。

【讨论】:

  • 我可以通过简化将行的前半部分交给线程 A,将另一半交给线程 B 进行 X 传递来使算法并行化。当我实现这一点时,我在每帧性能方面获得了小幅提升,这让我相信我并不是 100% 的带宽受限,只是大多数情况下。我见过使用递归加倍的 GPU 方法,但我发现来回移动数据的开销是我的限制因素。
猜你喜欢
  • 2014-10-22
  • 1970-01-01
  • 2013-05-23
  • 2019-03-31
  • 1970-01-01
  • 2014-09-08
  • 2013-12-25
  • 1970-01-01
  • 2022-09-23
相关资源
最近更新 更多