14

Intel i3 プロセッサで 32 要素 (各 1 バイト データ) の合計削減を見つけようとしています。これは私がしました:

s=0; 
for (i=0; i<32; i++)
{
    s = s + a[i];
}  

ただし、私のアプリケーションはリアルタイム アプリケーションなので、時間がかかります。最終的な合計が 255 を超える可能性があることに注意してください。

低レベルの SIMD SSE2 命令を使用してこれを実装する方法はありますか? 残念ながら、私は SSE を使用したことがありません。この目的でsse2関数を検索してみましたが、これも利用できません。このような小さなサイズの問題の計算時間を短縮することは (sse) 保証されていますか?

助言がありますか??

注: OpenCL と CUDA を使用して同様のアルゴリズムを実装しましたが、問題のサイズが大きい場合にのみうまく機能しました。小規模な問題の場合、オーバーヘッドのコストはより多くなりました。SSEでの動作がわからない

4

3 に答える 3

9

悪用PSADBWして、水平方向の小さな合計をすばやく計算できます。

このようなもの:(テストされていません)

pxor xmm0, xmm0
psadbw xmm0, [a + 0]
pxor xmm1, xmm1
psadbw xmm1, [a + 16]
paddw xmm0, xmm1
pshufd xmm1, xmm0, 2
paddw xmm0, xmm1 ; low word in xmm0 is the total sum

試行された組み込みバージョン:

私は組み込み関数を使用したことがないので、このコードはおそらくまったく意味がありません。しかし、分解はOKに見えました。

uint16_t sum_32(const uint8_t a[32])
{
    __m128i zero = _mm_xor_si128(zero, zero);
    __m128i sum0 = _mm_sad_epu8(
                        zero,
                        _mm_load_si128(reinterpret_cast<const __m128i*>(a)));
    __m128i sum1 = _mm_sad_epu8(
                        zero,
                        _mm_load_si128(reinterpret_cast<const __m128i*>(&a[16])));
    __m128i sum2 = _mm_add_epi16(sum0, sum1);
    __m128i totalsum = _mm_add_epi16(sum2, _mm_shuffle_epi32(sum2, 2));
    return totalsum.m128i_u16[0];
}
于 2012-06-07T14:11:28.227 に答える
5

これは少し時間がかかりますが、それでもスカラーコードよりも少なくとも2倍高速である必要があります。

uint16_t sum_32(const uint8_t a[32])
{
    const __m128i vk0 = _mm_set1_epi8(0);   // constant vector of all 0s for use with _mm_unpacklo_epi8/_mm_unpackhi_epi8
    __m128i v = _mm_load_si128(a);          // load first vector of 8 bit values
    __m128i vl = _mm_unpacklo_epi8(v, vk0); // unpack to two vectors of 16 bit values
    __m128i vh = _mm_unpackhi_epi8(v, vk0);
    __m128i vsum = _mm_add_epi16(vl, vh);
    v = _mm_load_si128(&a[16]);             // load second vector of 8 bit values
    vl = _mm_unpacklo_epi8(v, vk0);         // unpack to two vectors of 16 bit values
    vh = _mm_unpackhi_epi8(v, vk0);
    vsum = _mm_add_epi16(vsum, vl);
    vsum = _mm_add_epi16(vsum, vh);
    // horizontal sum
    vsum = _mm_add_epi16(vsum, _mm_srli_si128(vsum, 8));
    vsum = _mm_add_epi16(vsum, _mm_srli_si128(vsum, 4));
    vsum = _mm_add_epi16(vsum, _mm_srli_si128(vsum, 2));
    return _mm_extract_epi16(vsum, 0);
}

a[]16バイト整列する必要があることに注意してください。

おそらく、を使用して上記のコードを改善できます_mm_hadd_epi16

于 2012-06-07T15:20:44.010 に答える