Что вам по сути нужно сделать, это установить все байты (входных данных), которые вы хотите заменить на ноль. Затем установите все остальные байты подстановки в ноль и ИЛИ результаты. У вас уже есть маска для этого из _mm_cmpeq_epi8
. В целом, это можно сделать так:
__m128i mask = _mm_cmpeq_epi8(inp, val);
return _mm_or_si128(_mm_and_si128(mask, sub), _mm_andnot_si128(mask, inp));
Поскольку последняя комбинация и / и не / или очень распространена, SSE4.1 ввел инструкцию, которая (по существу) объединяет их в одну:
__m128i mask = _mm_cmpeq_epi8(inp, val);
return _mm_blendv_epi8(inp, sub, mask);
На самом деле, clang5.0 и более поздние версии достаточно умны, чтобы заменить первый вариант на второй при компиляции с оптимизацией: https://godbolt.org/z/P-tcik
Примечание: если значение подстановки на самом деле 0x01
, вы можете использовать тот факт, что маска (результат сравнения) равна 0x00
или 0xff
(то есть -0x01
), т. Е. Вы можете обнулите значения, которые вы хотите заменить, а затем вычтите маску:
__m128i val = _mm_set1_epi8(0x06);
__m128i mask = _mm_cmpeq_epi8(inp, val);
return _mm_sub_epi8(_mm_andnot_si128(mask, inp), mask);
Это может сохранить либо загрузку вектора 0x01
из памяти, либо потерю для него регистра. И в зависимости от вашей архитектуры он может иметь немного лучшую пропускную способность.