Согласно документации, от gcc 4.9
на AVX-512
набор инструкций поддерживается, но у меня есть gcc 4.8
, В настоящее время у меня есть такой код для суммирования блока памяти (он гарантированно будет меньше 256 байт, так что переполнения не волнует):
__mm128i sum = _mm_add_epi16(sum, _mm_cvtepu8_epi16(*(__m128i *) &mem));
Теперь, просматривая документацию, если у нас осталось, скажем, четыре байта, я мог бы использовать:
__mm128i sum = _mm_add_epi16(sum,
_mm_mask_cvtepu8_epi16(_mm_set1_epi16(0),
(__mmask8)_mm_set_epi16(0,0,0,0,1,1,1,1),
*(__m128i *) &mem));
(Обратите внимание, тип __mmask8
кажется, нигде не документирован, так что я думаю …)
Тем не мение, _mm_mask_cvtepu8_epi16
является AVX-512
инструкция, так есть ли способ продублировать это? Я старался:
mm_mullo_epi16(_mm_set_epi16(0,0,0,0,1,1,1,1),
_mm_cvtepu8_epi16(*(__m128i *) &mem));
Тем не менее, там была стойка с кешем, так что просто прямой for (int i = 0; i < remaining_bytes; i++) sum += mem[i];
дал лучшую производительность.
Как я случайно наткнулся на этот вопрос, и он до сих пор не получил ответа, если это все еще проблема …
Для вашего примера проблемы вы на правильном пути.
_mm_mullo_epi16
, использование _mm_and_si128
вместо этого, как побитовое И — намного более быстрая операция, например _mm_and_si128(_mm_cvtepu8_epi16(*(__m128i *) &mem), _mm_set_epi32(0, 0, -1, -1))
_mm_srli_si128(vector, 8)
который не требует никаких дополнительных регистров / загрузки памяти. Сдвиг может быть медленнее, чем AND._mm_move_epi64
n%16
байты для некоторого произвольного n
). _mm_and_si128(vector, masks[n & 0xf])
_mm_mask_cvtepu8_epi16
заботится только о нижней половине вектора, поэтому ваш пример несколько сбивает с толку — то есть вам не нужно ничего маскировать, потому что более поздние элементы полностью игнорируются)На более общем уровне операции маски на самом деле являются просто встроенными _mm_blend_epi16
(или эквивалент). Для обнуления идиом, они могут быть легко эмулированы с _mm_and_si128
/ _mm_andnot_si128
, как показано выше.
Других решений пока нет …