Если вам не нужно, чтобы выходные биты отображались точно в том же порядке, что и входные байты, но если вместо этого они могут «чередоваться» определенным образом, то быстрый и переносимый способ сделать это состоит в том, чтобы8 блоков по 8 байтов (всего 64 байта) и объединить все младшие биты в одно 8-байтовое значение.
Что-то вроде:
uint32_t extract_lsbs2(uint8_t (&input)[32]) {
uint32_t t0, t1, t2, t3, t4, t5, t6, t7;
memcpy(&t0, input + 0 * 4, 4);
memcpy(&t1, input + 1 * 4, 4);
memcpy(&t2, input + 2 * 4, 4);
memcpy(&t3, input + 3 * 4, 4);
memcpy(&t4, input + 4 * 4, 4);
memcpy(&t5, input + 5 * 4, 4);
memcpy(&t6, input + 6 * 4, 4);
memcpy(&t7, input + 7 * 4, 4);
return
(t0 << 0) |
(t1 << 1) |
(t2 << 2) |
(t3 << 3) |
(t4 << 4) |
(t5 << 5) |
(t6 << 6) |
(t7 << 7);
}
Это генерирует код "не ужасно, не замечательно" на большинстве компиляторов .
Если вы используете uint64_t
вместо uint32_t
это обычно будет в два раза быстрее (при условии, что у вас есть более 32 байтов для преобразования) на 64-битной платформе.
С SIMD вы можете легко векторизовать всю операцию за что-то вроде двухинструкции (для AVX2, но подойдет любой x86 SIMD ISA): сравните и pmovmskb
.