1

I am new to SSE and SSE2, and I wrote a small C sample (allocating two counters, one increasing other decreasing than adding the two), which is working as expected. I used intrinsics and Microsoft Visual Studio 10 C++ Express. As second step I wanted to understand what's going on under the hood, but I'm puzzled now. For example the assignment operation in the for loops compiles to:

__m128i a_ptr = _mm_load_si128((__m128i*)&(a_aligned[i]));
 mov         eax,dword ptr [i]  
 mov         ecx,dword ptr [a_aligned]  
 movdqa      xmm0,xmmword ptr [ecx+eax*2]  
 movdqa      xmmword ptr [ebp-1C0h],xmm0  
 movdqa      xmm0,xmmword ptr [ebp-1C0h]  
 movdqa      xmmword ptr [a_ptr],xmm0  

I understand that the first two lines gets the components of a_aligned's address, and the third line copies it to the xmm0 register. But I don't understand why it's copied back to memory, than to xmm0 again (than to a_ptr). I though that the _mm_load_si128 intrinsic should copy a_aligned[i]'s 128 bits to xmm0 and nothing more. Why is this happened? Am I wrong theoretically? If not how should I hint the compiler? Is my sample code correct (in sense that it doesn't have unnecessarities)? Here is my full sample code:

#include <xmmintrin.h>
#include <emmintrin.h>
#include <iostream>

int main(int argc, char *argv[]) {
    unsigned __int16 *a_aligned = (unsigned __int16 *)_mm_malloc(32 * sizeof(unsigned __int16),16);
    unsigned __int16 *b_aligned = (unsigned __int16 *)_mm_malloc(32 * sizeof(unsigned __int16),16);
    unsigned __int16 *c_aligned = (unsigned __int16 *)_mm_malloc(32 * sizeof(unsigned __int16),16);

    for(int i = 0; i < 32; i++) {
        a_aligned[i] = i;
        b_aligned[i] = i;
        c_aligned[i] = 0;
    }

    for(int i = 0; i < 32; i+=8) {
        __m128i a_ptr = _mm_load_si128((__m128i*)&(a_aligned[i]));
        __m128i b_ptr = _mm_load_si128((__m128i*)&(b_aligned[i]));
        __m128i res = _mm_add_epi16(a_ptr, b_ptr);
        _mm_store_si128((__m128i*)&(c_aligned[i]), res);
    }

    for(int i = 1; i < 32; i++) {
        std::cout << c_aligned[i] << " ";
    }

    _mm_free(a_aligned);
    _mm_free(b_aligned);
    _mm_free(c_aligned);
    return 0;
}
4

2 に答える 2

2

組み込み関数は、コンパイラ コード ジェネレーターがコードをより適切に最適化できるように明示的に設計されています。デバッグ構成によって生成されたアセンブリ コードを見ています。それは最適化されたコードではありません。リリース ビルドのコードを見てください。

        __m128i a_ptr = _mm_load_si128((__m128i*)&(a_aligned[i]));
011D10A0  movdqa      xmm0,xmmword ptr [eax] 
        __m128i b_ptr = _mm_load_si128((__m128i*)&(b_aligned[i]));
011D10A4  movdqa      xmm1,xmmword ptr [edx+eax] 
        __m128i res = _mm_add_epi16(a_ptr, b_ptr);
011D10A9  paddw       xmm0,xmm1 
        _mm_store_si128((__m128i*)&(c_aligned[i]), res);
011D10AD  movdqa      xmmword ptr [ecx+eax],xmm0 

見栄えが良くなりましたね。

于 2011-08-08T21:50:03.480 に答える
1

コンパイラ設定で最適化を有効にします (デバッグの代わりにリリース構成を使用します)。

于 2011-08-08T21:47:38.973 に答える