Заменить байт другим - PullRequest
       21

Заменить байт другим

0 голосов
/ 15 января 2019

Я нахожу трудности в создании кода для этой, казалось бы, легкой проблемы.

Учитывая упакованное 8-битное целое число, замените один байт другим, если он присутствует.

Например, я хочу заменить 0x06 на 0x01, поэтому я могу сделать следующее с res в качестве ввода для поиска 0x06:

// Bytes to be manipulated
res = _mm_set_epi8(0x00, 0x03, 0x02, 0x06, 0x0F, 0x02, 0x02, 0x06, 0x0A, 0x03, 0x02, 0x06, 0x00, 0x00, 0x02, 0x06);

// Target value and substitution
val = _mm_set1_epi8(0x06);
sub = _mm_set1_epi8(0x01);

// Find the target
sse = _mm_cmpeq_epi8(res, val);

// Isolate target
sse = _mm_and_si128(res, sse);

// Isolate remaining bytes
adj = _mm_andnot_si128(sse, res);

Теперь я не знаю, как перейти к or этим двум частям, мне нужно удалить цель и заменить ее замененным байтом.

Какую SIMD-инструкцию мне здесь не хватает?

Как и в случае с другими вопросами, я ограничен AVX, у меня нет лучшего процессора.

1 Ответ

0 голосов
/ 16 января 2019

Что вам по сути нужно сделать, это установить все байты (входных данных), которые вы хотите заменить на ноль. Затем установите все остальные байты подстановки в ноль и ИЛИ результаты. У вас уже есть маска для этого из _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 из памяти, либо потерю для него регистра. И в зависимости от вашей архитектуры он может иметь немного лучшую пропускную способность.

...