У меня есть случай использования, где у меня есть массив битов, каждый бит представлен как 8-битное целое число, например uint8_t data[] = {0,1,0,1,0,1,0,1};
Я хочу создать одно целое число, извлекая только lsb каждого значения. Я знаю что используя int _mm_movemask_pi8 (__m64 a)
Функция Я могу создать маску, но эта внутренняя занимает только байт байта, а не lsb. Существует ли подобный внутренний или эффективный метод для извлечения lsb для создания одного 8-битного целого числа?
Нет прямого способа сделать это, но, очевидно, вы можете просто сдвинуть 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
Если у вас эффективный ИМТ2 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;
}
это компилируется так, как вы ожидаете к словесной нагрузке + а pext
инструкция. Компиляторы поднимут 0x01...
постоянная настройка вне цикла после встраивания.
pext
/ pdep
эффективны на процессорах Intel, которые их поддерживают (задержка 3 цикла / пропускная способность 1с, 1 моп, то же, что и умножение). Но они не эффективный на AMD, как задержка 18c и пропускная способность. (https://agner.org/optimize/). Если вы заботитесь о AMD, вам обязательно стоит использовать @ harold’s pmovmskb
ответ.
Или, если у вас есть несколько смежных блоков по 8 байт, сделайте их с одним широким вектором и получите 32-битное растровое изображение. Вы можете разделить это, если необходимо, или развернуть цикл, используя 4, чтобы сместить вправо растровое изображение и получить все 4 однобайтовых результата.
Если вы просто сохраняете это в памяти сразу, то вам, вероятно, следовало бы выполнить это извлечение в цикле, который записывал исходные данные, а не в отдельном цикле, поэтому он все еще будет горячим в кеше. AVX2 _mm256_movemask_epi8
это один моп (на процессорах Intel) с низкой задержкой, поэтому, если ваши данные не находятся в кэш-памяти L1d, то цикл, который просто делает это не будет держать свои исполнительные блоки заняты в ожидании памяти.