@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;
}
コンパイルおよび上記テストハーネスを実行します。
を我々は16ビットをアンパックし、蓄積する
_mm_madd_epi16
を使用して上記SSEコード内の1つの可能性の非自明なトリックがあること
$ 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
)ということを利用しています。ここでは、実際には1つの命令でアンパックして合計しているわけではありません。
UPDATE:以下のコメントで述べたように、このソリューションが最適ではない - 私はかなり最適な16ビット・ソリューションを取り、それが8ビットのデータのために動作させるために、開梱16ビットに8ビットを追加しました。しかしながら、8ビットデータの場合、より効率的な方法が存在する。 psadbw
/_mm_sad_epu8
を使用してください。私はこの回答を残念ながら残しておきますが、このようなことを16ビットデータでやりたい人にとっては、実際には入力データを解凍する必要のない答えの1つが受け入れられた答えになるはずです。
残りのバイトが16より小さい場合は、forループ(長さ/ 16回)を使用し、lhsと1からrhsに0を埋め込みます。パディングは、パディングを誤って数えないように異なる必要があります。 –
'while(length> = 16){/ *あなたの関数を使う*/length - = 16; }長さ(最大15バイト)を比較するバージョンを使う* /; ' – pmg
FYIこれはしばしば[*ハミング距離*]と呼ばれます(http://en.wikipedia.org/wiki/Hamming_distance ) - これは検索語として有用かもしれません。 –