2013-07-28 15 views
13

同じ長さ(たとえば、m)のすべての文字列、同じ長さの別の文字列sがあります。n(8ビット)の文字列があります。 sから他の各文字列までのハミング距離を計算する必要があります。プレーンCのようなもの:SSEで複数の文字列へのハミング距離を計算する

unsigned char strings[n][m]; 
unsigned char s[m]; 
int distances[n]; 

for(i=0; i<n; i++) { 
    int distances[i] = 0; 
    for(j=0; j<m; j++) { 
    if(strings[i][j] != s[j]) 
     distances[i]++; 
    } 
} 

このような計算をより効率的に実行するには、gccでSIMD命令を使用したいと思います。私はSSE 4.2でPcmpIstrIが有用であり、ターゲットコンピュータがその命令セットをサポートしていると読んだので、SSE 4.2を使用するソリューションを好むでしょう。

EDIT:

私は2つの文字列の間のハミング距離を計算するために次の関数を書いた:だから私はによって私の問題を解決することができ

static inline int popcnt128(__m128i n) { 
    const __m128i n_hi = _mm_unpackhi_epi64(n, n); 
    return _mm_popcnt_u64(_mm_cvtsi128_si64(n)) + _mm_popcnt_u64(_mm_cvtsi128_si64(n_hi)); 
} 

int HammingDist(const unsigned char *p1, unsigned const char *p2, const int len) { 
#define MODE (_SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_EACH | _SIDD_BIT_MASK | _SIDD_NEGATIVE_POLARITY) 
    __m128i smm1 = _mm_loadu_si128 ((__m128i*) p1); 
    __m128i smm2 = _mm_loadu_si128 ((__m128i*) p2); 
    __m128i ResultMask; 

    int iters = len/16; 
    int diffs = 0; 
    int i; 

    for(i=0; i<iters; i++) { 
    ResultMask = _mm_cmpestrm (smm1,16,smm2,16,MODE); 

    diffs += popcnt128(ResultMask); 
    p1 = p1+16; 
    p2 = p2+16; 
    smm1 = _mm_loadu_si128 ((__m128i*)p1); 
    smm2 =_mm_loadu_si128 ((__m128i*)p2); 
    } 

    int mod = len % 16; 
    if(mod>0) { 
    ResultMask = _mm_cmpestrm (smm1,mod,smm2,mod,MODE); 
    diffs += popcnt128(ResultMask); 
    } 

    return diffs; 
} 

for(i=0; i<n; i++) { 
    int distances[i] = HammingDist(s, strings[i], m); 
} 

はこれです私ができることは最高ですか、比較した文字列のうちの1つが常に同じであるという事実を使用できますか?また、パフォーマンスを向上させるために配列に整列を行う必要がありますか?

ハロルドのrecomendation続いて別の試み

は、私は次のコードを書かれている:

void _SSE_hammingDistances(const ByteP str, const ByteP strings, int *ds, const int n, const int m) { 
    int iters = m/16; 

    __m128i *smm1, *smm2, diffs; 

    for(int j=0; j<n; j++) { 
     smm1 = (__m128i*) str; 
     smm2 = (__m128i*) &strings[j*(m+1)]; // m+1, as strings are '\0' terminated 

     diffs = _mm_setzero_si128(); 

     for (int i = 0; i < iters; i++) { 
      diffs = _mm_add_epi8(diffs, _mm_cmpeq_epi8(*smm1, *smm2)); 
      smm1 += 1; 
      smm2 += 1; 
     } 

     int s = m; 
     signed char *ptr = (signed char *) &diffs; 
     for(int p=0; p<16; p++) { 
      s += *ptr; 
      ptr++; 
     } 

     *ds = s; 
     ds++; 
    } 
} 

を私はpsadbwを使用して__m128iのバイトの最後の追加を行うことができないのです。誰もそれで私を助けてくれる?

+2

あなたの質問は正確に何ですか? – andy256

+2

実際、 'pcmpistri'はまったく役に立ちません。この場合、必要なのは普通の' pcmpeqb'です。そして、あなたはそのpopcnt-stuffのどれも必要としません。カウントからの比較の結果を差し引くだけです(結果が-1のため、結果は-1です)。そして最後にpsadbwを実行します。 4Kバイトを処理する直前に 'psadbw'を使用しています) – harold

+0

psadbwを使用することはできませんでしたが、haroldに感謝の意を表しました。 – pepeStck

答えて

2

ここでスカラーコードを排除するPSADBWを使用して最新のルーチンの改良版、(_mm_sad_epu8)です:

void hammingDistances_SSE(const uint8_t * str, const uint8_t * strings, int * const ds, const int n, const int m) 
{ 
    const int iters = m/16; 

    const __m128i smm1 = _mm_loadu_si128((__m128i*)str); 

    assert((m & 15) == 0);  // m must be a multiple of 16 

    for (int j = 0; j < n; j++) 
    { 
     __m128i smm2 = _mm_loadu_si128((__m128i*)&strings[j*(m+1)]); // m+1, as strings are '\0' terminated 

     __m128i diffs = _mm_setzero_si128(); 

     for (int i = 0; i < iters; i++) 
     { 
      diffs = _mm_sub_epi8(diffs, _mm_cmpeq_epi8(smm1, smm2)); 
     } 

     diffs = _mm_sad_epu8(diffs, _mm_setzero_si128()); 
     ds[j] = m - (_mm_extract_epi16(diffs, 0) + _mm_extract_epi16(diffs, 4)); 
    } 
} 
関連する問題