1

我有一个矢量化优化问题。

我有一个结构 pDst,它有 3 个字段名为:“红色”、“绿色”和“蓝色”。
类型可能是“Char”、“Short”或“Float”。这是给定的,不能更改。
还有另一个数组 pSrc 表示图像 [RGB] - 即一个由 3 个指针组成的数组,每个指针都指向图像的一层。
每一层都是使用面向 IPP 平面的图像构建的(即,每个平面都是独立形成的 - 'ippiMalloc_32f_C1'): http ://software.intel.com/sites/products/documentation/hpc/ipp/ippi/ippi_ch3/functn_Malloc.html .

我们希望按照以下代码中的说明复制它:

for(int y = 0; y < imageHeight; ++y)
{
    for(int x = 0; x < imageWidth; ++x)
    {
        pDst[x + y * pDstRowStep].red     = pSrc[0][x + y * pSrcRowStep];
        pDst[x + y * pDstRowStep].green   = pSrc[1][x + y * pSrcRowStep];
        pDst[x + y * pDstRowStep].blue    = pSrc[2][x + y * pSrcRowStep];
    }
} 

然而,在这种形式下,编译器不能向量化代码。
起初它说:

“循环未矢量化:存在矢量依赖性。”。

当我使用#pragma ivdep 来帮助编译器时(因为没有依赖关系),我收到以下错误:

“循环未矢量化:取消引用太复杂。”。

任何人都知道如何允许矢量化?
我使用英特尔编译器 13.0。
谢谢。

更新:

如果我编辑代码如下:

Ipp32f *redChannel      = pSrc[0];
Ipp32f *greenChannel  = pSrc[1];
Ipp32f *blueChannel     = pSrc[2];
for(int y = 0; y < imageHeight; ++y)
{
    #pragma ivdep
    for(int x = 0; x < imageWidth; ++x)
    {
        pDst[x + y * pDstRowStep].red     = redChannel[x + y * pSrcRowStep];
        pDst[x + y * pDstRowStep].green   = greenChannel[x + y * pSrcRowStep];
        pDst[x + y * pDstRowStep].blue    = blueChannel[x + y * pSrcRowStep];
    }
}

对于'char'和'short'的输出类型,我得到了vecotization。
然而,对于“浮动”类型,我没有。
相反,我收到以下消息:

循环未矢量化:矢量化可能但似乎效率低下。

怎么可能?

4

2 回答 2

1

这些方面的东西应该可以工作(char版本,未经测试,还请记住 __m128i 指针应该正确对齐!)

void interleave_16px_to_rgb0(__m128i *red, __m128i *green, __m128i *blue, __m128i *dest) {
  __m128i zero = _mm_setzero_si128();
  __m128i rg_0 = _mm_unpackhi_epi8(*red, *green);
  __m128i rg_1 = _mm_unpacklo_epi8(*red, *green);
  __m128i bz_0 = _mm_unpackhi_epi8(*blue, zero);
  __m128i bz_1 = _mm_unpacklo_epi8(*blue, zero);
  dest[0] = _mm_unpackhi_epi16(rg_0, bz_0);
  dest[1] = _mm_unpacklo_epi16(rg_0, bz_0);
  dest[2] = _mm_unpackhi_epi16(rg_1, bz_1);
  dest[3] = _mm_unpacklo_epi16(rg_1, bz_1);
}

这将从每个平面占用 16 个字节:

r0 r1 r2 ... r16
g0 g1 g2 ... g16
b0 b1 b2 ... b16

并像这样交错它们,从以下开始写出 16x4 字节*dest

r0 g0 b0 0 r1 g1 b1 0 r2 g2 b2 0 ... r16 g16 b16 0

不用说,您也可以使用相同的函数族来交错其他数据类型。


更新:更好的是,由于您已经拥有 IPP,您应该尝试使用所提供的内容,而不是重新发明轮子。通过快速检查,您正在寻找 ippiCopy_8u_P3C3R 或 ippiCopy_8u_P4C4R 。

于 2012-10-03T12:54:43.627 回答
1

在以下代码中,使用 pragma ivdep 肯定会忽略向量依赖性,但编译器启发式/成本分析得出的结论是向量化循环效率不高:

Ipp32f *redChannel      = pSrc[0];
Ipp32f *greenChannel  = pSrc[1];
Ipp32f *blueChannel     = pSrc[2];
for(int y = 0; y < imageHeight; ++y)
{
    #pragma ivdep
    for(int x = 0; x < imageWidth; ++x)
    {
        pDst[x + y * pDstRowStep].red     = redChannel[x + y * pSrcRowStep];
        pDst[x + y * pDstRowStep].green   = greenChannel[x + y * pSrcRowStep];
        pDst[x + y * pDstRowStep].blue    = blueChannel[x + y * pSrcRowStep];
    }
}

矢量化将是低效的,因为该操作涉及将连续的内存块从源复制到目标的非连续内存位置。所以这里发生了分散。如果您仍想强制执行矢量化并查看与非矢量化版本相比是否有任何性能改进,请使用 pragma simd 而不是 pragma ivdep,如下所示:

#include<ipp.h>
struct Dest{
float red;
float green;
float blue;
};
void foo(Dest *pDst, Ipp32f **pSrc, int imageHeight, int imageWidth, int pSrcRowStep, int pDstRowStep){
    Ipp32f *redChannel      = pSrc[0];
    Ipp32f *greenChannel  = pSrc[1];
    Ipp32f *blueChannel     = pSrc[2];
    for(int y = 0; y < imageHeight; ++y)
    {
        #pragma simd
        for(int x = 0; x < imageWidth; ++x)
        {
            pDst[x + y * pDstRowStep].red     = redChannel[x + y * pSrcRowStep];
            pDst[x + y * pDstRowStep].green   = greenChannel[x + y * pSrcRowStep];
            pDst[x + y * pDstRowStep].blue    = blueChannel[x + y * pSrcRowStep];
        }
    }
    return;
}

对应的矢量化报告为:

$ icpc -c test.cc -vec-report2
test.cc(14): (col. 9) remark: SIMD LOOP WAS VECTORIZED
test.cc(11): (col. 5) remark: loop was not vectorized: not inner loop

有关 pragma simd 的更多文档,请访问https://software.intel.com/en-us/node/514582

于 2014-05-07T17:05:55.127 回答