SSE双线性插值

7
我正在紧密循环中实现双线性插值,并尝试使用SSE优化它,但我没有从中得到任何加速。
这是代码,非SIMD版本使用一个简单的向量结构,可以定义为struct Vec3f { float x,y,z; },并实现了乘法和加法运算符:
#ifdef USE_SIMD
    const Color c11 = pixelCache[y1 * size.x + x1];
    const Color c12 = pixelCache[y2 * size.x + x1];
    const Color c22 = pixelCache[y2 * size.x + x2];
    const Color c21 = pixelCache[y1 * size.x + x2];

    __declspec(align(16)) float mc11[4] = { 1.0, c11.GetB(), c11.GetG(), c11.GetR() };
    __declspec(align(16)) float mc12[4] = { 1.0, c12.GetB(), c12.GetG(), c12.GetR() };
    __declspec(align(16)) float mc22[4] = { 1.0, c22.GetB(), c22.GetG(), c22.GetR() };
    __declspec(align(16)) float mc21[4] = { 1.0, c21.GetB(), c21.GetG(), c21.GetR() };

    // scalars in vector form for SSE
    const float s11 = (x2-x)*(y2-y);
    const float s12 = (x2-x)*(y-y1);
    const float s22 = (x-x1)*(y-y1);
    const float s21 = (x-x1)*(y2-y);

    __declspec(align(16)) float ms11[4] = {1.0, s11, s11, s11};
    __declspec(align(16)) float ms12[4] = {1.0, s12, s12, s12};
    __declspec(align(16)) float ms22[4] = {1.0, s22, s22, s22};
    __declspec(align(16)) float ms21[4] = {1.0, s21, s21, s21};

    __asm {
        movaps xmm0, mc11
        movaps xmm1, mc12
        movaps xmm2, mc22
        movaps xmm3, mc21

        movaps xmm4, ms11
        movaps xmm5, ms12
        movaps xmm6, ms22
        movaps xmm7, ms21

        mulps xmm0, xmm4
        mulps xmm1, xmm5
        mulps xmm2, xmm6
        mulps xmm3, xmm7

        addps xmm0, xmm1
        addps xmm0, xmm2
        addps xmm0, xmm3

        movaps mc11, xmm0
    }
#else
    const Vec3f c11 = toFloat(pixelCache[y1 * size.x + x1]);
    const Vec3f c12 = toFloat(pixelCache[y2 * size.x + x1]);
    const Vec3f c22 = toFloat(pixelCache[y2 * size.x + x2]);
    const Vec3f c21 = toFloat(pixelCache[y1 * size.x + x2]);

    const Vec3f colour =
            c11*(x2-x)*(y2-y) +
            c21*(x-x1)*(y2-y) +
            c12*(x2-x)*(y-y1) +
            c22*(x-x1)*(y-y1);
#endif

重新排列汇编代码以重复使用寄存器(最终只使用了三个xmm寄存器)没有产生任何效果。我还尝试使用内部函数:
// perform bilinear interpolation
const Vec3f c11 = toFloat(pixelCache[y1 * size.x + x1]);
const Vec3f c12 = toFloat(pixelCache[y2 * size.x + x1]);
const Vec3f c22 = toFloat(pixelCache[y2 * size.x + x2]);
const Vec3f c21 = toFloat(pixelCache[y1 * size.x + x2]);

// scalars in vector form for SSE
const float s11 = (x2-x)*(y2-y);
const float s12 = (x2-x)*(y-y1);
const float s22 = (x-x1)*(y-y1);
const float s21 = (x-x1)*(y2-y);

__m128 mc11 = _mm_set_ps(1.f, c11.b, c11.g, c11.r);
__m128 mc12 = _mm_set_ps(1.f, c12.b, c12.g, c12.r);
__m128 mc22 = _mm_set_ps(1.f, c22.b, c22.g, c22.r);
__m128 mc21 = _mm_set_ps(1.f, c21.b, c21.g, c21.r);

__m128 ms11 = _mm_set_ps(1.f, s11, s11, s11);
__m128 ms12 = _mm_set_ps(1.f, s12, s12, s12);
__m128 ms22 = _mm_set_ps(1.f, s22, s22, s22);
__m128 ms21 = _mm_set_ps(1.f, s21, s21, s21);

mc11 = _mm_mul_ps(mc11, ms11);
mc12 = _mm_mul_ps(mc12, ms12);
mc22 = _mm_mul_ps(mc22, ms22);
mc21 = _mm_mul_ps(mc21, ms21);

mc11 = _mm_add_ps(mc11, mc12);
mc11 = _mm_add_ps(mc11, mc22);
mc11 = _mm_add_ps(mc11, mc21);

Vec3f colour;
_mm_storeu_ps(colour.array, mc11);

而且没有任何效果。我是否漏掉了什么,或者在这里获得额外的速度是不可能的?

8
很抱歉,但这绝对不是正确的方法。首先,你需要花费很多力气来填充向量(这纯粹是开销)。然后,在计算结束时会出现一个讨厌的依赖链。但根本上,主要问题在于你正在使用结构体数组打包。如果你真的想用SIMD,应该考虑切换到数组结构体。 - Mysticial
好的,我明白了关于填充向量的问题,我会尝试首先重新排列数据以尊重对齐。但是您能否详细说明一下“计算结束时的依赖链”是什么意思? - SimpleMan
7
你有3个相互依赖的加法。因此,它们中的任何一个都不能并行完成,因为必须在开始下一个之前完成上一个。我看到你正在进行一些简化 - 最好使用二叉树简化来完成。现在你只是组合了4个向量。因此,无论如何重新排列,都没有太大的收益。但我怀疑在更大的情况下,你实际上是在对一组更大的数字求和。 - Mysticial
2
除非你在进行HDR,否则没有理由选择浮点数而不是定点8.8。 - Aki Suihkonen
我实际上建议您保留C++版本,但添加基于内联指令的Vec3f操作实现。这样,您可以在保持可维护代码的同时获得一些加速。 - andrewmu
你为什么要使用浮点数? - user1401452
1个回答

8

为什么使用浮点数?

假设有a、b、c、d和xerr、yerr这6个变量,它们的取值范围是0-256。以压缩像素argb为例:

// =================================================================================================================
// xs_Bilerp
// =================================================================================================================
finline uint32 xs_Bilerp (uint32 a, uint32 b, uint32 c, uint32 d, uint32 xerr, uint32 yerr)
{
    #define xs_rbmask    0x00ff00ff
    #define xs_agmask    0xff00ff00

    if (a==b && c==d && a==d)   return a;

    const uint32 arb        =   a & xs_rbmask;
    const uint32 crb        =   c & xs_rbmask;
    const uint32 aag        =   a & xs_agmask;
    const uint32 cag        =   c & xs_agmask;

    const uint32 rbdx1      =  (b & xs_rbmask) - arb;
    const uint32 rbdx2      =  (d & xs_rbmask) - crb;
    const uint32 agdx1      = ((b & xs_agmask)>>8) - (aag >> 8);
    const uint32 agdx2      = ((d & xs_agmask)>>8) - (cag >> 8);

    const uint32 rb1        = (arb      + ((rbdx1 * xerr) >> 8)) & xs_rbmask;
    const uint32 ag1        = (aag      + ((agdx1 * xerr)     )) & xs_agmask;
    const uint32 rbdy       = ((crb     + ((rbdx2 * xerr) >> 8)) & xs_rbmask)       - rb1;
    const uint32 agdy       = (((cag    + ((agdx2 * xerr)     )) & xs_agmask)>>8)   - (ag1 >> 8);

    const uint32 rb         = (rb1 + ((rbdy * yerr) >> 8)) & xs_rbmask;
    const uint32 ag         = (ag1 + ((agdy * yerr)     )) & xs_agmask;

    return ag | rb;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接