2013-07-12 60 views
3

有兩種方法在使用SSE內在內存寄存器的相互作用:SSE加載/存儲內存交易

中級指針:

void f_sse(float *input, float *output, unsigned int n) 
{ 
    _m128 *input_sse = reinterpret_cast<__m128*>(input);//Input intermediate pointer 
    _m128 *output_sse = reinterpret_cast<__m128*>(output);//Output intermediate pointer 
    _m128 s = _mm_set1_ps(0.1f); 
    auto loop_size = n/4; 
    for(auto i=0; i<loop_size; ++i) 
     output_sse[i] = _mm_add_ps(input_sse[i], s); 
} 

顯式讀取/存儲:

void f_sse(float *input, float *output, unsigned int n) 
{ 
    _m128 input_sse, output_sse, result; 
    _m128 s = _mm_set1_ps(0.1f); 
    for(auto i=0; i<n; i+=4) 
    { 
     input_sse = _mm_load_ps(input+i); 
     result  = _mm_add_ps(input_sse, s); 
     _mm_store_ps(output+i, result); 
    } 
} 

什麼所提及的方法與哪種方法在性能方面更好?輸入和輸出指針由_mm_malloc()對齊。

+0

如果第一個例子的賦值操作使用未對齊的指令,它將會變慢。 _mm_store_ps對齊存儲不是嗎?第一個例子與元素複製類似。你可以顯示反彙編輸出? –

回答

1

編譯時的g ++優化級別O3(使用objdump -d)內循環的彙編代碼是

20: 0f 28 04 07    movaps (%rdi,%rax,1),%xmm0 
24: 0f 58 c1    addps %xmm1,%xmm0 
27: 0f 29 04 06    movaps %xmm0,(%rsi,%rax,1) 
2b: 48 83 c0 10    add $0x10,%rax 
2f: 48 39 d0    cmp %rdx,%rax 
32: 75 ec     jne 20 <_Z5f_ssePfS_j+0x20> 

10: 0f 28 04 07    movaps (%rdi,%rax,1),%xmm0 
14: 83 c1 04    add $0x4,%ecx 
17: 0f 58 c1    addps %xmm1,%xmm0 
1a: 0f 29 04 06    movaps %xmm0,(%rsi,%rax,1) 
1e: 48 83 c0 10    add $0x10,%rax 
22: 39 ca     cmp %ecx,%edx 
24: 77 ea     ja  10 <_Z5f_ssePfS_j+0x10> 

他們是非常相似的。在第一個g ++中設法只使用一個計數器(只有一個add指令)。所以我想它更好。

1

我使用g ++ -O2編譯了兩個樣本,我發現的主要區別是edx(n)中的值的使用方式不同,導致代碼略有不同。

第一功能:

0000000000000000 <_Z6f_sse2PfS_j>: 
    0: c1 ea 02    shr $0x2,%edx  # loop_size = n/4. 
    3: 85 d2     test %edx,%edx 
    5: 74 2d     je  34 <_Z6f_sse2PfS_j+0x34> 
    7: 83 ea 01    sub $0x1,%edx 
    a: 0f 28 0d 00 00 00 00 movaps 0x0(%rip),%xmm1  # 11 <_Z6f_sse2PfS_j+0x11> 
    11: 48 83 c2 01    add $0x1,%rdx 
    15: 31 c0     xor %eax,%eax 
    17: 48 c1 e2 04    shl $0x4,%rdx    // Adjust for loop size vs. index. 
    1b: 0f 1f 44 00 00   nopl 0x0(%rax,%rax,1) 
    20: 0f 28 04 07    movaps (%rdi,%rax,1),%xmm0 
    24: 0f 58 c1    addps %xmm1,%xmm0 
    27: 0f 29 04 06    movaps %xmm0,(%rsi,%rax,1) 
    2b: 48 83 c0 10    add $0x10,%rax 
    2f: 48 39 d0    cmp %rdx,%rax 
    32: 75 ec     jne 20 <_Z6f_sse2PfS_j+0x20> 
    34: f3 c3     repz retq 

二級功能:

0000000000000000 <_Z5f_ssePfS_j>: 
    0: 85 d2     test %edx,%edx 
    2: 74 22     je  26 <_Z5f_ssePfS_j+0x26> 
    4: 0f 28 0d 00 00 00 00 movaps 0x0(%rip),%xmm1  # b <_Z5f_ssePfS_j+0xb> 
    b: 31 c0     xor %eax,%eax 
    d: 31 c9     xor %ecx,%ecx 
    f: 90      nop 
    10: 0f 28 04 07    movaps (%rdi,%rax,1),%xmm0 
    14: 83 c1 04    add $0x4,%ecx 
    17: 0f 58 c1    addps %xmm1,%xmm0 
    1a: 0f 29 04 06    movaps %xmm0,(%rsi,%rax,1) 
    1e: 48 83 c0 10    add $0x10,%rax 
    22: 39 ca     cmp %ecx,%edx 
    24: 77 ea     ja  10 <_Z5f_ssePfS_j+0x10> 
    26: f3 c3     repz retq 

我又看了看生成的代碼,以及與此想出了:

void f_sse2(float *input, float *output, unsigned int n) 
{ 
    __m128 *end = reinterpret_cast<__m128*>(&input[n]); 
    __m128 *input_sse = reinterpret_cast<__m128*>(input);//Input intermediate pointer 
    __m128 *output_sse = reinterpret_cast<__m128*>(output);//Output intermediate pointer 
    __m128 s = _mm_set1_ps(0.1f); 
    while(input_sse < end) 
     *output_sse++ = _mm_add_ps(*input_sse++, s); 
} 

產生這樣的代碼:

0000000000000000 <_Z6f_sse2PfS_j>: 
    0: 89 d2     mov %edx,%edx 
    2: 48 8d 04 97    lea (%rdi,%rdx,4),%rax 
    6: 48 39 c7    cmp %rax,%rdi 
    9: 73 23     jae 2e <_Z6f_sse2PfS_j+0x2e> 
    b: 0f 28 0d 00 00 00 00 movaps 0x0(%rip),%xmm1  # 12 <_Z6f_sse2PfS_j+0x12> 
    12: 66 0f 1f 44 00 00  nopw 0x0(%rax,%rax,1) 
    18: 0f 28 07    movaps (%rdi),%xmm0 
    1b: 48 83 c7 10    add $0x10,%rdi 
    1f: 0f 58 c1    addps %xmm1,%xmm0 
    22: 0f 29 06    movaps %xmm0,(%rsi) 
    25: 48 83 c6 10    add $0x10,%rsi 
    29: 48 39 f8    cmp %rdi,%rax 
    2c: 77 ea     ja  18 <_Z6f_sse2PfS_j+0x18> 
    2e: f3 c3     repz retq 

我認爲這可能更有效一點,但可能不值得改變它。但它給了我15分鐘的時間。