Алгоритм MIT было бы сложно реализовать в SSE2, так как не существует инструкции целочисленного модуля, которую можно было бы использовать для конечного выражения ... % 255
. Из различных методов popcnt, тот, который наиболее легко и эффективно поддается SSE, является, вероятно, первым в главе 5 «Хакерского наслаждения» Генри С. Уоррена , которую я реализовал здесь C с использованием встроенных функций SSE:
#include <stdio.h>
#include <emmintrin.h>
__m128i _mm_popcnt_epi16(__m128i v)
{
v = _mm_add_epi16(_mm_and_si128(v, _mm_set1_epi16(0x5555)), _mm_and_si128(_mm_srli_epi16(v, 1), _mm_set1_epi16(0x5555)));
v = _mm_add_epi16(_mm_and_si128(v, _mm_set1_epi16(0x3333)), _mm_and_si128(_mm_srli_epi16(v, 2), _mm_set1_epi16(0x3333)));
v = _mm_add_epi16(_mm_and_si128(v, _mm_set1_epi16(0x0f0f)), _mm_and_si128(_mm_srli_epi16(v, 4), _mm_set1_epi16(0x0f0f)));
v = _mm_add_epi16(_mm_and_si128(v, _mm_set1_epi16(0x00ff)), _mm_and_si128(_mm_srli_epi16(v, 8), _mm_set1_epi16(0x00ff)));
return v;
}
int main(void)
{
__m128i v0 = _mm_set_epi16(7, 6, 5, 4, 3, 2, 1, 0);
__m128i v1;
v1 = _mm_popcnt_epi16(v0);
printf("v0 = %vhd\n", v0);
printf("v1 = %vhd\n", v1);
return 0;
}
Скомпилируйте и протестируйте следующим образом:
$ gcc -Wall -msse2 _mm_popcnt_epi16.c -o _mm_popcnt_epi16
$ ./_mm_popcnt_epi16
v0 = 0 1 2 3 4 5 6 7
v1 = 0 1 1 2 1 2 2 3
$
Похоже, около 16 арифметических / логических инструкций, поэтому он должен работать примерно на 16/8 = 2 такта на точку.
Вы можете легко преобразовать это в необработанный ассемблер, если вам нужно - каждая внутренняя карта отображается в одну инструкцию.