Устранение перемежения вектора полубайтов с помощью SIMD - PullRequest
2 голосов
/ 01 августа 2020

У меня есть входной вектор из 16384 четырехбитных целых чисел со знаком. Они упакованы в 8192 байта. Мне нужно чередовать значения и распаковать в 8-битные целые числа со знаком в двух отдельных массивах.

a, b, c, d - 4-битные значения. A, B, C, D - 8-битные значения.

Input = [ab, cd, ...] Out_1 = [A, C, ...] Out_2 = [B, D, ...]

Я могу сделать это довольно легко на C ++.

constexpr size_t size = 32768;
int8_t input[size]; // raw packed 4bit integers
int8_t out_1[size];
int8_t out_2[size];

for (int i = 0; i < size; i++) {
    out_1[i] = input[i] << 4;
    out_1[i] = out_1[i] >> 4;
    out_2[i] = input[i] >> 4;
}

Я хотел бы реализовать это, чтобы работать как можно быстрее в общих целях процессоры. Существуют хорошие реализации SIMD 8-битного деинтерлейва в 16-битные целые числа, например, в VOLK, но я не могу найти даже базовых c операторов побайтного сдвига SIMD.

https://github.com/gnuradio/volk/blob/master/kernels/volk/volk_8ic_deinterleave_16i_x2.h#L63

Спасибо!

1 Ответ

1 голос
/ 01 августа 2020

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

const __m128i mm_mask = _mm_set1_epi32(0x0F0F0F0F);
const __m128i mm_signed_max = _mm_set1_epi32(0x07070707);

for (size_t i = 0u, n = size / 16u; i < n; ++i)
{
    // Load and deinterleave input half-bytes
    __m128i mm_input_even = _mm_loadu_si128(reinterpret_cast< const __m128i* >(input) + i);
    __m128i mm_input_odd = _mm_srli_epi32(mm_input_even, 4);

    mm_input_even = _mm_and_si128(mm_input_even, mm_mask);
    mm_input_odd = _mm_and_si128(mm_input_odd, mm_mask);

    // If you need sign extension, you need the following
    // Get the sign bits
    __m128i mm_sign_even = _mm_cmpgt_epi8(mm_input_even, mm_signed_max);
    __m128i mm_sign_odd = _mm_cmpgt_epi8(mm_input_odd, mm_signed_max);

    // Combine sign bits with deinterleaved input
    mm_input_even = _mm_or_si128(mm_input_even, _mm_andnot_si128(mm_mask, mm_sign_even));
    mm_input_odd = _mm_or_si128(mm_input_odd, _mm_andnot_si128(mm_mask, mm_sign_odd));

    // Store the results
    _mm_storeu_si128(reinterpret_cast< __m128i* >(out_1) + i, mm_input_even);
    _mm_storeu_si128(reinterpret_cast< __m128i* >(out_2) + i, mm_input_odd);
}

Если ваш size не кратен 16, вам также нужно добавить обработку хвостовых байтов. Вы можете использовать для этого свой невекторизованный код.

Обратите внимание, что в приведенном выше коде вам не нужны побайтовые сдвиги, так как вам все равно нужно применить маску. Так что здесь подойдут любые более грубые сдвиги.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...