Как создать 8-битную маску из lsb значения __m64? - PullRequest
0 голосов
/ 30 августа 2018

У меня есть случай использования, где у меня есть массив битов, каждый бит представлен как 8-битное целое число, например, uint8_t data[] = {0,1,0,1,0,1,0,1}; Я хочу создать одно целое число, извлекая только lsb каждого значения. Я знаю, что с помощью функции int _mm_movemask_pi8 (__m64 a) я могу создать маску, но эта внутренняя функция занимает только байт байта, а не lsb. Существует ли подобный внутренний или эффективный метод для извлечения lsb для создания одного 8-битного целого числа?

Ответы [ 2 ]

0 голосов
/ 30 августа 2018

Если у вас эффективный BMI2 pext (например, Haswell и новее, такой же, как AVX2), тогда используйте обратный ответ @ wim на ваш вопрос о том, чтобы идти в другом направлении ( Как эффективно преобразовать 8-битный растровое изображение в массив целых чисел 0/1 с x86 SIMD ).

unsigned extract8LSB(uint8_t *arr) {
    uint64_t bytes;
    memcpy(&bytes, arr, 8);
    unsigned LSBs = _pext_u64(bytes ,0x0101010101010101);
    return LSBs;
}

Этот компилируется так, как вы ожидаете для загрузки qword + инструкции pext. Компиляторы будут выводить постоянную настройку 0x01... из цикла после вставки.


pext / pdep эффективны на процессорах Intel, которые их поддерживают (задержка 3 такта / пропускная способность 1с, 1 моп, аналогично умножению). Но они не эффективны на AMD, как задержка 18c и пропускная способность. (https://agner.org/optimize/). Если вам небезразлична AMD, вам обязательно следует использовать ответ pmovmskb @ harold.

Или, если у вас есть несколько смежных блоков по 8 байт, сделайте их с одним широким вектором и получите 32-битное растровое изображение. Вы можете разделить это, если необходимо, или развернуть цикл, используя 4, чтобы сдвинуть вправо растровое изображение и получить все 4 однобайтовых результата.

Если вы просто сохраняете это в памяти сразу, то вам, вероятно, следовало бы выполнить это извлечение в цикле, который записывал исходные данные, а не в отдельном цикле, так что он все еще будет горячим в кеше. AVX2 _mm256_movemask_epi8 - это один моп (на процессорах Intel) с низкой задержкой, поэтому, если ваши данные не находятся в кэш-памяти L1d, тогда цикл, который только делает это, не будет сохранять занятость его исполнительных блоков, пока в ожидании памяти.

0 голосов
/ 30 августа 2018

Нет прямого способа сделать это, но, очевидно, вы можете просто сдвинуть lsb в msb и затем извлечь его:

_mm_movemask_pi8(_mm_slli_si64(x, 7))

Использование MMX в наши дни странно, и его, вероятно, следует избегать.

Вот версия SSE2, которая все еще читает только 8 байтов:

int lsb_mask8(uint8_t* bits) {
    __m128i x = _mm_loadl_epi64((__m128i*)bits);
    return _mm_movemask_epi8(_mm_slli_epi64(x, 7));
}

Использование SSE2 вместо MMX устраняет необходимость в EMMS

...