@Mysticial 위의 의견에 말했듯이, 수행 비교하고 수직으로 요약 한 후 바로 메인 루프의 끝에서 수평으로 요약 :
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <emmintrin.h>
// reference implementation
int fast_compare_ref(const char *s, const char *t, int length)
{
int result = 0;
int i;
for (i = 0; i < length; ++i)
{
if (s[i] == t[i])
result++;
}
return result;
}
// optimised implementation
int fast_compare(const char *s, const char *t, int length)
{
int result = 0;
int i;
__m128i vsum = _mm_set1_epi32(0);
for (i = 0; i < length - 15; i += 16)
{
__m128i vs, vt, v, vh, vl, vtemp;
vs = _mm_loadu_si128((__m128i *)&s[i]); // load 16 chars from input
vt = _mm_loadu_si128((__m128i *)&t[i]);
v = _mm_cmpeq_epi8(vs, vt); // compare
vh = _mm_unpackhi_epi8(v, v); // unpack compare result into 2 x 8 x 16 bit vectors
vl = _mm_unpacklo_epi8(v, v);
vtemp = _mm_madd_epi16(vh, vh); // accumulate 16 bit vectors into 4 x 32 bit partial sums
vsum = _mm_add_epi32(vsum, vtemp);
vtemp = _mm_madd_epi16(vl, vl);
vsum = _mm_add_epi32(vsum, vtemp);
}
// get sum of 4 x 32 bit partial sums
vsum = _mm_add_epi32(vsum, _mm_srli_si128(vsum, 8));
vsum = _mm_add_epi32(vsum, _mm_srli_si128(vsum, 4));
result = _mm_cvtsi128_si32(vsum);
// handle any residual bytes (< 16)
if (i < length)
{
result += fast_compare_ref(&s[i], &t[i], length - i);
}
return result;
}
// test harness
int main(void)
{
const int n = 1000000;
char *s = malloc(n);
char *t = malloc(n);
int i, result_ref, result;
srand(time(NULL));
for (i = 0; i < n; ++i)
{
s[i] = rand();
t[i] = rand();
}
result_ref = fast_compare_ref(s, t, n);
result = fast_compare(s, t, n);
printf("result_ref = %d, result = %d\n", result_ref, result);;
return 0;
}
컴파일하고 위의 테스트 장치 실행
을 우리가 풀고
_mm_madd_epi16
를 사용하여 16 비트를 축적 위의 SSE 코드의 하나의 가능성이 아닌 명백한 속임수가 있다는
$ gcc -Wall -O3 -msse3 fast_compare.c -o fast_compare
$ ./fast_compare
result_ref = 3955, result = 3955
$ ./fast_compare
result_ref = 3947, result = 3947
$ ./fast_compare
result_ref = 3945, result = 3945
는
주 0
/-1
값은 32 비트 부분 합계입니다. -1*-1 = 1
(및 0*0 = 0
물론)을 활용합니다. 여기서는 실제로 곱셈을하지 않고 하나의 명령어로 풀고 요약하는 것입니다.
UPDATE : 아래의 코멘트에 언급 한 바와 같이,이 솔루션이 최적이 아닌 - 난 그냥 상당히 최적의 16 비트 솔루션을 가져다가 8 개 비트 데이터를 작동하도록 풀고 16 비트에 8 비트를 추가했다. 그러나 8 비트 데이터의 경우보다 효율적인 방법이 있습니다. psadbw
/_mm_sad_epu8
을 사용하십시오. 나는이 대답을 후손을 위해 남겨두고, 16 비트 데이터로 이런 종류의 일을하고 싶어하는 사람들을 위해, 그러나 실제로 입력 데이터를 푸는 것을 요구하지 않는 다른 해답 중 하나는 받아 들여진 대답이어야한다.
는 패딩이 달라야 루프 우측에 좌측 및 것들 (길이/16 시간), 및 패드 0을 사용. –
'while (길이> = 16) {/ * 함수 사용 */길이 - = 16; } 길이 (최대 길이)/* 길이 (최대 15) 바이트를 비교하는 버전 사용 * /; ' – pmg
FYI 이것은 종종 [* 해밍 거리 *]라고합니다 (http://en.wikipedia.org/wiki/Hamming_distance) - 검색 용어로 유용 할 수 있습니다. –