循环矢量化001

时间:2012-10-03 11:28:20

标签: c++ c optimization vectorization icc

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

我有一个结构pDst,它有3个名为'red','green'和'blue'的字段 类型可能是“Char”,“Short”或“Float”。这是给出的,不能改变 Theres是另一个数组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 然而,对于'浮动'的类型,我不这样做 相反,我收到以下消息:

  

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

怎么可能?

2 个答案:

答案 0 :(得分: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

并像这样交错,从*dest开始写出16x4字节:

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

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


更新:更好,因为你已经有了IPP,你应该尝试使用提供的内容而不是重新发明轮子。从快速检查看来,ippiCopy_8u_P3C3R或ippiCopy_8u_P4C4R正是您所寻找的。

答案 1 :(得分: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

https://software.intel.com/en-us/node/514582提供了有关pragma simd的更多文档。