Вопрос

Позвольте мне предисловите это с .. У меня чрезвычайно ограниченный опыт с ASM, а еще меньше с SIMD.

Но это происходит, что у меня есть следующий код MMX / SSE Code, который я хотел бы портировать в соответствии с инструкциями 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;
}

Любые советы о том, как я могу переписать это, чтобы использовать инструкции Attivec?

Моя первая попытка (очень неверная попытка) выглядит что-то вроде этого .. Но это не совсем (или даже удаленно) правильно.

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;
}
Это было полезно?

Решение

Вы не далее - я исправил несколько незначительных проблем, немного очистил код, добавил тестовый жгут, и кажется, что сейчас работает:

#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;
}

Другие советы

(Предупреждение: все мой опыт Altivec приходит от работы на Xbox360 / PS3 - я не уверен, насколько они отличаются от других платформ Altivec).

Во-первых, вы должны проверить ваше выравнивание указателя. Ожидается, что большинство векторов (и магазинов) операций ожидается от 16-байтовых выровненных адресов. Если они не будут, все обычно не будут продолжаться без предупреждения, но вы не получите данные, которые вы ожидаете.

Возможно (но медленнее), чтобы сделать безгнистые нагрузки, но вы в основном должны читать немного до и после ваших данных и комбинировать их. Видеть Страница Alble's Altivec. Отказ Я также сделал это, прежде чем использовать lvlx а также lvrx Нагружать инструкции, а затем или их вместе.


Далее я не уверен, что ваши умножки и добавления одинаковы. Я никогда не использовал либо _mm_madd_pi16, либо vec_msum, поэтому я не уверен, что они эквивалентны. Вы должны пройти в отладчик и убедиться, что они дают вам тот же выход для тех же входных данных. Другим возможным отличием состоит в том, что они могут лечить переполнение по-разному (например, модульные против насыщения).


Последнее, но не менее важное, вы вычисляете 4 INT в течение времени, а не 2. Итак, ваш союз должен держать 4 INT, и вы должны суммировать все 4 из них в конце.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top