将MMX / SSE指令移植到AltiVec

时间:2010-12-04 01:23:10

标签: simd mmx altivec

让我先说一下..我对ASM的经验非常有限,而SIMD的经验则更少。

但碰巧我有以下MMX / SSE优化代码,我希望将其移植到AltiVec指令以便在PPC / Cell处理器上使用。

这可能是一个很大的问题..即使只有几行代码,我也没有遇到麻烦,试图弄清楚这里发生了什么。

原始功能:

static inline int convolve(const short *a, const short *b, int n)
{
    int out = 0;
    union {
        __m64 m64;
        int i32[2];
    } tmp;
    tmp.i32[0] = 0;
    tmp.i32[1] = 0;
    while (n >= 4) {
        tmp.m64 = _mm_add_pi32(tmp.m64,
                               _mm_madd_pi16(*((__m64 *)a),
                                             *((__m64 *)b)));
        a += 4;
        b += 4;
        n -= 4;
    }
    out = tmp.i32[0] + tmp.i32[1];
    _mm_empty();

    while (n --)
        out += (*(a++)) * (*(b++));
    return out;
}

有关如何重写此内容以使用AltiVec说明的任何提示?

我的第一次尝试(非常错误的尝试)看起来像这样..但它并不完全(甚至远程)正确。

static inline int convolve_altivec(const short *a, const short *b, int n)
{
    int out = 0;
    union {
        vector unsigned int m128;
        int i64[2];
    } tmp;

    vector unsigned int zero = {0, 0, 0, 0};

    tmp.i64[0] = 0;
    tmp.i64[1] = 0;
    while (n >= 8) {
        tmp.m128 = vec_add(tmp.m128,
                               vec_msum(*((vector unsigned short *)a),
                                             *((vector unsigned short *)b), zero));

        a += 8;
        b += 8;
        n -= 8;
    }
    out = tmp.i64[0] + tmp.i64[1];
#endif
    while (n --)
        out += (*(a++)) * (*(b++));
    return out;
}

2 个答案:

答案 0 :(得分:3)

你离我不远 - 我解决了一些小问题,稍微清理了一下代码,添加了一个测试工具,现在看起来工作正常了:

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <altivec.h>

static int convolve_ref(const short *a, const short *b, int n)
{
    int out = 0;
    int i;

    for (i = 0; i < n; ++i)
    {
        out += a[i] * b[i];
    }

    return out;
}

static inline int convolve_altivec(const short *a, const short *b, int n)
{
    int out = 0;
    union {
        vector signed int m128;
        int i32[4];
    } tmp;

    const vector signed int zero = {0, 0, 0, 0};

    assert(((unsigned long)a & 15) == 0);
    assert(((unsigned long)b & 15) == 0);

    tmp.m128 = zero;

    while (n >= 8)
    {
        tmp.m128 = vec_msum(*((vector signed short *)a),
                            *((vector signed short *)b), tmp.m128);

        a += 8;
        b += 8;
        n -= 8;
    }

    out = tmp.i32[0] + tmp.i32[1] + tmp.i32[2] + tmp.i32[3];

    while (n --)
        out += (*(a++)) * (*(b++));

    return out;
}

int main(void)
{
    const int n = 100;

    vector signed short _a[n / 8 + 1];
    vector signed short _b[n / 8 + 1];

    short *a = (short *)_a;
    short *b = (short *)_b;

    int sum_ref, sum_test;

    int i;

    for (i = 0; i < n; ++i)
    {
        a[i] = rand();
        b[i] = rand();
    }

    sum_ref = convolve_ref(a, b, n);
    sum_test = convolve_altivec(a, b, n);

    printf("sum_ref = %d\n", sum_ref);
    printf("sum_test = %d\n", sum_test);

    printf("%s\n", sum_ref == sum_test ? "PASS" : "FAIL");

    return 0;
}

答案 1 :(得分:1)

(警告:我的所有Altivec经验来自于使用Xbox360 / PS3 - 我不确定它们与其他Altivec平台有多么不同。)

首先,您应该检查指针对齐方式。大多数向量加载(和存储)操作应该来自16字节对齐的地址。如果不是,事情通常会在没有警告的情况下继续进行,但是你不会得到你期望的数据。

执行未对齐的加载是可能的(但速度较慢),但您基本上必须在数据之前和之后读取并合并它们。见Apple's Altivec page。我在使用lvlxlvrx加载指令之前完成了它,然后将它们进行OR运算。


接下来,我不确定你的乘法和加法是否相同。我从来没有使用_mm_madd_pi16或vec_msum,所以我不肯定他们是等同的。您应该在调试器中单步执行并确保它们为相同的输入数据提供相同的输出。另一个可能的区别是它们可能不同地处理溢出(例如模块化与饱和)。


最后但并非最不重要的是,你一次计算4个整数而不是2个。所以你的联盟应该保持4个整数,你应该在最后总计4个整数。