Имитация инструкции маски AVX-512

Согласно документации, от 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]; дал лучшую производительность.

1

Решение

Как я случайно наткнулся на этот вопрос, и он до сих пор не получил ответа, если это все еще проблема …

Для вашего примера проблемы вы на правильном пути.

  • Умножение является относительно медленной операцией, поэтому следует избегать использования _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.
  • Если это всегда 8 байтов, вы можете использовать _mm_move_epi64
  • Ничто из этого не решает проблему, если оставшееся число не является фиксированным числом элементов (например, у вас есть n%16 байты для некоторого произвольного n). Обратите внимание, что AVX-512 тоже не решает эту проблему. Если вам нужно разобраться с этим делом, вы можете иметь таблицу масок и AND в зависимости от того, что осталось, например, _mm_and_si128(vector, masks[n & 0xf])
  • (_mm_mask_cvtepu8_epi16 заботится только о нижней половине вектора, поэтому ваш пример несколько сбивает с толку — то есть вам не нужно ничего маскировать, потому что более поздние элементы полностью игнорируются)

На более общем уровне операции маски на самом деле являются просто встроенными _mm_blend_epi16 (или эквивалент). Для обнуления идиом, они могут быть легко эмулированы с _mm_and_si128 / _mm_andnot_si128, как показано выше.

2

Другие решения

Других решений пока нет …