有效地对整个内存块进行位移位

  • 本文关键字:内存 有效地 c++ memory
  • 更新时间 :
  • 英文 :


执行 sobel 操作后,我有以下代码:

short* tempBufferVert = new short[width * height];
ippiFilterSobelVertBorder_8u16s_C1R(pImg, width, tempBufferVert, width * 2, dstSize, IppiMaskSize::ippMskSize3x3, IppiBorderType::ippBorderConst, 0, pBufferVert);
for (int i = 0; i < width * height; i++)
    tempBufferVert[i] >>= 2;

令人沮丧的是,位移位是所有操作中最长的,IPP sobel 非常优化,它比我愚蠢的位移运行得更快。如何优化位移,或者是否有 IPP 或其他选项(AVX?)对整个内存执行位移(但与>>= 在 Visual Studio 实现上执行的短字符有关)

C++优化器使用基于迭代器的循环比使用索引循环的性能要好得多。

这是因为编译器可以假设地址算术在索引溢出时的工作方式。为了在数组中使用索引时做出相同的假设,您必须碰巧为索引选择正确的数据类型。

移位代码可以表示为:

void shift(short* first, short* last, int bits)
{
  while (first != last) {
    *first++ >>= bits;
  }
}
int test(int width, int height)
{
  short* tempBufferVert = new short[width * height];
  shift(tempBufferVert, tempBufferVert + (width * height), 2);
}

哪个(启用正确的优化)将被矢量化:https://godbolt.org/g/oJ8Boj

注意循环的中间是如何变成的:

.L76:
        vmovdqa ymm0, YMMWORD PTR [r9+rdx]
        add     r8, 1
        vpsraw  ymm0, ymm0, 2
        vmovdqa YMMWORD PTR [r9+rdx], ymm0
        add     rdx, 32
        cmp     rsi, r8
        ja      .L76
        lea     rax, [rax+rdi*2]
        cmp     rcx, rdi
        je      .L127
        vzeroupper
首先,

请确保您在编译时启用了优化(例如 -O3 ),然后检查编译器是否正在自动矢量化右移位循环。如果不是,那么使用 SSE 您可能会获得显着的改进:

#include <emmintrin.h> // SSE2
for (int i = 0; i < width * height; i += 8)
{
    __m128i v = _mm_loadu_si128((__m128i *)&tempBufferVert[i]);
    v = _mm_srai_epi16(v, 2); // v >>= 2
    _mm_storeu_si128((__m128i *)&tempBufferVert[i], v);
}

(注意:假设width*height是 8 的倍数。

通过一些循环展开和/或使用 AVX2,您可能会做得更好,但这可能足以满足您的需求。

最新更新