【问题标题】:Block Matching optimization using x86/x64 Streaming SIMD Extension使用 x86/x64 流 SIMD 扩展的块匹配优化
【发布时间】:2013-04-03 22:31:23
【问题描述】:

这将是我发布的第一个 SO 问题!

    std::cout << "Hello mighty StackOverflow!" << std::endl;

我正在尝试使用英特尔的 SSE4.2 和/或 AVX 内在函数优化立体视觉应用的“块匹配”实现。我正在使用“绝对差异之和”来找到最佳匹配块。在我的情况下,blockSize 将是一个 odd 数字,例如 3 或 5。这是我的 C++ 代码的 sn-p:

    for (int i = 0; i < rows; ++i) {
        for (int j = 0; j < cols; ++j) {
            minS = INT_MAX;
            for (int k = 0; k <= beta; ++k) {
                S = 0;
                for (int l = i; l < i + blockSize; ++l) {
                    for (int m = j; m <= j + blockSize ; ++m) {
                        // adiff(a,b) === abs(a-b)
                        S += adiff(rImage.at<uchar>(l, m), lImage.at<uchar>(l, m + k));
                    }
                }
                if (S < minS) {
                    minS = S;
                    kStar = k;
                }
            }
            disparity.at<uchar>(i, j) = kStar;
        }
    }

我知道 Streaming SIMD 扩展包含许多指令以促进使用 SAD 进行块匹配,例如 _mm_mpsadbw_epu8_mm_sad_epu8 ,但它们都以 blockSizes 为目标,即 4、16 或 32。@ 987654321@。我的问题是在我的应用程序中blockSize 是一个奇数,主要是 3 或 5。

我考虑了以下起点:

            r0 = _mm_lddqu_si128 ((__m128i*)&rImage.at<uchar>(i, j));
            l0 = _mm_lddqu_si128 ((__m128i*)&lImage.at<uchar>(i, j));
            s0 = _mm_abs_epi8 (_mm_sub_epi8 (r0 , l0) );

但是从这里开始,我不知道如何从s0 中总结 3 或 5 个连续字节!

我将不胜感激对此的任何想法。

【问题讨论】:

  • 欢迎来到 Stack Overflow!对于新成员来说,这是一个精心设计的问题。我想你很快就会得到一个好的答案。 :)
  • 您只对(水平)差异感兴趣吗?
  • 我怀疑最好的答案是简单地用零填充你的行/列(这样这些条目对你的最终总和没有任何贡献)。
  • 总和将超过 3x3 或 5x5 块 - 即 1+8 和 1+24 项加在一起。 8 和 24 OTOH 是可以使用 SIMD 有效处理的数字。
  • 是的@AkiSuihkonen。我假设之前已经对数据进行了调平。

标签: c++ c optimization sse simd


【解决方案1】:

我怀疑如果块大小小到 3-5 字节 x 3-5 字节,那么使用 SSE 或类似指令您将获得的好处很少,因为您将花费太多的“收益”来做数学快速“混合”(将数据从一个地方移动到另一个地方)。

但是,查看代码,您似乎正在多次处理相同的rImage[i, j],我认为这没有意义。

【讨论】:

  • ... 除非运行时数据缓存系统能够成功预测哪些数据块应该提前直接带入 L1 甚至可能是 L2 缓存。从代码的外观来看,数据块可以在循环中以可预测的方式访问,因此运行时系统可能能够消除这种混乱的开销,并且代码可能会受益于 SIMD 内在函数。
  • 是的,swizzling 的问题不在于缓存依赖,而是因为您需要额外的指令来执行移动数据的操作。而且我很确定 rImage(i,j) 在第一个循环之后在缓存中,但它仍然是额外的指令来再次计算这些字节的总和。
  • 我很好奇你怀疑数据被移动到哪里(假设你的意思是S += ... 行导致数据被移动(而不是仅仅从缓存中读取,可能进入寄存器 - 或你的意思是从缓存读取数据到寄存器的过程是一个瓶颈数据“swizzle”?))。
  • 不,我的意思是,如果您尝试将 3 x 3 方形像素模式中的 9 个字节放入 SSE 或 AVX 字中,则需要将字节打乱到各自的位置。为了合理使用 SSE 指令,它们需要包含多个可以执行相同操作的数据项。否则不是“单指令多数据”。当然,您一次可以占用 3 个字节,但是否有意义还有待商榷。
  • @MatsPetersson:到目前为止,我使用 3x4 块(而不是 3x3)作为近似值进行的一项实验表明,在 GCC 和 ICC 上进行编译器优化后性能提高了 30x .参考代码也进行了一些指针调整。我也没有看到这里有什么令人兴奋的事情,因为访问是按整体升序排列的,我猜缓存将能够轻松地处理那里的几个背部和堡垒。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-11-24
  • 2019-05-29
  • 1970-01-01
  • 2021-12-26
  • 2011-07-11
  • 2020-12-05
  • 1970-01-01
相关资源
最近更新 更多