2010-05-27 277 views
6

我有一些代碼在一個循環SSE SIMD優化For循環

for(int i = 0; i < n; i++) 
{ 
    u[i] = c * u[i] + s * b[i]; 
} 

所以,u和b是相同的長度的矢量,並且c和s是標量。這個代碼是否適合與SSE一起使用以加速矢量化?

UPDATE

我學到矢量(原來,這並不難,如果您使用內聯函數),並實現我的SSE循環。但是,在VC++編譯器中設置SSE2標誌時,我獲得的性能與我自己的SSE代碼大致相同。另一方面,英特爾編譯器比我的SSE代碼或VC++編譯器快得多。

這裏是我寫的參考

double *u = (double*) _aligned_malloc(n * sizeof(double), 16); 
for(int i = 0; i < n; i++) 
{ 
    u[i] = 0; 
} 

int j = 0; 
__m128d *uSSE = (__m128d*) u; 
__m128d cStore = _mm_set1_pd(c); 
__m128d sStore = _mm_set1_pd(s); 
for (j = 0; j <= i - 2; j+=2) 
{ 
    __m128d uStore = _mm_set_pd(u[j+1], u[j]); 

    __m128d cu = _mm_mul_pd(cStore, uStore); 
    __m128d so = _mm_mul_pd(sStore, omegaStore); 

    uSSE[j/2] = _mm_add_pd(cu, so); 
} 
for(; j <= i; ++j) 
{ 
    u[j] = c * u[j] + s * omegaCache[j]; 
} 
+0

[注VC11現在在它的優化使用SIMD(http://blogs.microsoft.co.il/blogs/sasha/archive/2011/10/17/simd-optimized-c-code-in -visual-studio-11.aspx) – bobobobo 2012-10-13 17:30:00

回答

5

是的,這是一個優秀的矢量化候選人。但是,在你這樣做之前,確保你已經對你的代碼進行了簡介,以確保這實際上是值得優化的。這就是說,矢量化會去是這樣的:

int i; 
for(i = 0; i < n - 3; i += 4) 
{ 
    load elements u[i,i+1,i+2,i+3] 
    load elements b[i,i+1,i+2,i+3] 
    vector multiply u * c 
    vector multiply s * b 
    add partial results 
    store back to u[i,i+1,i+2,i+3] 
} 

// Finish up the uneven edge cases (or skip if you know n is a multiple of 4) 
for(; i < n; i++) 
    u[i] = c * u[i] + s * b[i]; 

爲了獲得更大的性能,可以考慮預取進一步的數組元素,和/或展開循環使用software pipelining與內存交錯在一個循環計算從不同的迭代訪問。

+0

明確發現此代碼是一個瓶頸。檢查我學習和實現向量化的問題不是浪費的努力 - 編譯器通常不會自動將這些代碼自動向量化? – 2010-05-27 04:17:23

+1

@Projectile如果你告訴編譯器關於混疊,通常它會。根據我自己的經驗,生成比編譯器更好的代碼是非常不尋常的,不需要付出非常大的努力。 – Anycorn 2010-05-27 04:26:48

-1

代碼這取決於你如何放置u和b在內存中。 如果兩個內存塊彼此距離很遠,在這種情況下SSE不會提升太多。

建議數組u和b是AOE(數組結構)而不是SOA(數組結構),因爲您可以將它們兩個都加載到單指令寄存器中。

+1

我不同意在這裏使用AOS比SOA更有優勢。你仍然爲每家商店做2次裝載,而使用AOS,你現在必須在每4個裝置中只寫回2次。使用SOA,您可以從'u'加載4個單元,從'b'加載4個單元,然後將4寫回到'u',而無需執行任何混洗或屏蔽。 – 2010-05-27 04:00:03

+0

兩點都不同意YeenFei。對於垂直SIMD,SOA通常更勝一籌。由於緩存,內存距離並不是一個相關因素 - 即使AOS允許使用較少的緩存行(例如,由於極端的對齊填充,這本質上不太可能發生在SIMD化的良好候選中),但重構這些仍然會更好緩存線是SOA。 – mabraham 2012-11-14 12:26:31

1

可能是的,但你必須幫助編譯器提供一些提示。放在指針上的 __restrict__告訴編譯器兩個指針之間沒有別名。 如果你知道你的向量對齊,將其傳遞給編譯器(Visual C++可能有一些設施)。

我自己對Visual C++並不熟悉,但我聽說它對矢量化沒有好處。 請考慮使用英特爾編譯器。 Intel允許對生成的程序集進行相當細緻的控制:http://www.intel.com/software/products/compilers/docs/clin/main_cls/cref_cls/common/cppref_pragma_vector.htm

+1

誰比他們更瞭解英特爾處理器? :) – YeenFei 2010-05-27 05:05:03

1

_mm_set_pd未被矢量化。如果從字面上看,它使用標量操作讀取兩個雙精度值,然後結合兩個標量雙精度值並將它們複製到SSE寄存器中。改爲使用_mm_load_pd

1

是的,這是一個很棒的候選vectorizaton,假設沒有U和B數組的重疊。但是代碼受內存訪問(加載/存儲)的約束。向量化有助於減少每個循環的週期,但由於U和B陣列上的緩存未命中,指令會停止。英特爾C/C++編譯器使用Xeon x5500處理器的默認標誌生成以下代碼。編譯器將循環展開8,並使用xmm [0-16] SIMD寄存器採用SIMD ADD(addpd)和MULTIPLY(mulpd)指令。在每個週期中,假設您已經在寄存器中準備好數據,處理器可以發出2個SIMD指令,產生4路標量ILP。

這裏U,B,C和S是雙精度(8字節)。

..B1.14:      # Preds ..B1.12 ..B1.10 
    movaps %xmm1, %xmm3         #5.1 
    unpcklpd %xmm3, %xmm3         #5.1 
    movaps %xmm0, %xmm2         #6.12 
    unpcklpd %xmm2, %xmm2         #6.12 
     # LOE rax rcx rbx rbp rsi rdi r8 r12 r13 r14 r15 xmm0 xmm1 xmm2 xmm3 
    ..B1.15:  # Preds ..B1.15 ..B1.14 
    movsd  (%rsi,%rcx,8), %xmm4       #6.21 
    movhpd 8(%rsi,%rcx,8), %xmm4       #6.21 
    mulpd  %xmm2, %xmm4         #6.21 
    movaps (%rdi,%rcx,8), %xmm5       #6.12 
    mulpd  %xmm3, %xmm5         #6.12 
    addpd  %xmm4, %xmm5         #6.21 
    movaps 16(%rdi,%rcx,8), %xmm7      #6.12 
    movaps 32(%rdi,%rcx,8), %xmm9      #6.12 
    movaps 48(%rdi,%rcx,8), %xmm11      #6.12 
    movaps %xmm5, (%rdi,%rcx,8)       #6.3 
    mulpd  %xmm3, %xmm7         #6.12 
    mulpd  %xmm3, %xmm9         #6.12 
    mulpd  %xmm3, %xmm11         #6.12 
    movsd  16(%rsi,%rcx,8), %xmm6      #6.21 
    movhpd 24(%rsi,%rcx,8), %xmm6      #6.21 
    mulpd  %xmm2, %xmm6         #6.21 
    addpd  %xmm6, %xmm7         #6.21 
    movaps %xmm7, 16(%rdi,%rcx,8)      #6.3 
    movsd  32(%rsi,%rcx,8), %xmm8      #6.21 
    movhpd 40(%rsi,%rcx,8), %xmm8      #6.21 
    mulpd  %xmm2, %xmm8         #6.21 
    addpd  %xmm8, %xmm9         #6.21 
    movaps %xmm9, 32(%rdi,%rcx,8)      #6.3 
    movsd  48(%rsi,%rcx,8), %xmm10      #6.21 
    movhpd 56(%rsi,%rcx,8), %xmm10      #6.21 
    mulpd  %xmm2, %xmm10         #6.21 
    addpd  %xmm10, %xmm11        #6.21 
    movaps %xmm11, 48(%rdi,%rcx,8)      #6.3 
    addq  $8, %rcx          #5.1 
    cmpq  %r8, %rcx          #5.1 
    jl  ..B1.15  # Prob 99%      #5.1