Загрузка данных для векторных расширений GCC - PullRequest
8 голосов
/ 16 февраля 2012

GCC векторных расширений предлагают хороший, достаточно портативный способ доступа к некоторым инструкциям SIMD на разных аппаратных архитектурах, не прибегая к аппаратным характеристикам (или автоматической векторизации).

Реальным вариантом использования является вычисление простой аддитивной контрольной суммы.Единственное, что неясно, это как безопасно загружать данные в вектор.

typedef char v16qi __attribute__ ((vector_size(16)));

static uint8_t checksum(uint8_t *buf, size_t size)
{
    assert(size%16 == 0);
    uint8_t sum = 0;

    vec16qi vec = {0};
    for (size_t i=0; i<(size/16); i++)
    {
        // XXX: Yuck! Is there a better way?
        vec += *((v16qi*) buf+i*16);
    }

    // Sum up the vector
    sum = vec[0] + vec[1] + vec[2] + vec[3] + vec[4] + vec[5] + vec[6] + vec[7] + vec[8] + vec[9] + vec[10] + vec[11] + vec[12] + vec[13] + vec[14] + vec[15];

    return sum;
}

Кажется, что приведение указателя к типу вектора работает, но я боюсь, что это может ужасно взорватьсяесли SIMD ожидает, что векторные типы будут правильно выровнены.

Единственный другой вариант, о котором я подумал, - это использовать временный вектор и явно загружать значения (с помощью memcpy или поэлементного присваивания), нопри тестировании это противодействовало большей части ускорения, полученного с использованием SIMD-инструкций.В идеале я бы предположил, что это будет что-то вроде общей __builtin_load() функции, но, похоже, ее не существует.

Какой более безопасный способ загрузки данных в вектор с риском выравнивания?

Ответы [ 2 ]

1 голос
/ 21 сентября 2016

Редактировать (спасибо Питер Кордес) Вы можете разыгрывать указатели:

typedef char v16qi __attribute__ ((vector_size (16), aligned (16)));

v16qi vec = *(v16qi*)&buf[i]; // load
*(v16qi*)(buf + i) = vec; // store whole vector

Это компилируется в vmovdqa для загрузки и vmovups для хранения. Если данные не известны для выравнивания, установите aligned (1) для генерации vmovdqu. (* 1 010 * godbolt )

Обратите внимание, что есть также несколько специальных встроенных функций для загрузки и выгрузки этих регистров ( Edit 2 ):

v16qi vec = _mm_loadu_si128((__m128i*)&buf[i]); // _mm_load_si128 for aligned
_mm_storeu_si128((__m128i*)&buf[i]), vec); // _mm_store_si128 for aligned

Кажется необходимым использовать -flax-vector-conversions для перехода от char с к v16qi с этой функцией.

См. Также: C - Как получить доступ к элементам вектора с помощью расширения вектора SSE GCC
См. Также: SSE, загрузка в __m128

(Совет. Лучшая фраза для Google - что-то вроде "gcc loading __m128i".)

0 голосов
/ 03 октября 2012

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

const vec16qi e = { buf[0], buf[1], ... , buf[15] }

и надеемся, что GCC превратит это в инструкцию загрузки SSE. Я бы проверил это с помощью дизассемблера ;-). Кроме того, для повышения производительности вы пытаетесь выровнять buf 16-байтовых данных и сообщить об этом компилятору с помощью атрибута aligned. Если вы можете гарантировать, что входной буфер будет выровнен, обрабатывайте его побайтово, пока не достигнете 16-байтовой границы.

...