【问题标题】:Loop Vectorization 001循环矢量化 001
【发布时间】:2012-09-24 07:48:28
【问题描述】:

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

我有一个结构 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。
但是对于“浮动”类型,我没有。
相反,我收到以下消息:

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

怎么可能?

【问题讨论】:

  • 在目前的形式下,它不会对你所做的任何事情进行矢量化,因为你在这里进行“收集”操作。
  • 这就是我要找的,哪种形式可以实现矢量化?谢谢。

标签: c++ c optimization vectorization icc


【解决方案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 获得。

【讨论】:

    【解决方案2】:

    类似的东西应该可以工作(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。

    【讨论】:

    • 不过,我不只是尝试交错,要注意输出数据结构。我不认为它与 IPP 中的完全一样。
    • 我所能看到的只是跨步的交错。你的意思是什么?
    • 另外,我很想注意输出数据结构。太糟糕了,你没有包括在你的问题中...... :-P
    • 嗨,我写了关于 pDst 的文章,它是一个结构(不是数组,它是结构数组)。它不是一个数组,我可以假设像素在内存中彼此相邻。只有结构数组是连续的。
    • 要么你过早地进行优化,在这种情况下你应该立即停止,或者你没有。在后一种情况下,您需要准备好更改数据结构。
    猜你喜欢
    • 2020-10-02
    • 2021-03-04
    • 1970-01-01
    • 1970-01-01
    • 2016-06-18
    • 2016-02-11
    • 2014-09-25
    • 2017-03-13
    • 1970-01-01
    相关资源
    最近更新 更多