2012-03-20 56 views
10

我有一個__m256d矢量,它包含四個64位浮點值。
我需要找到向量元素的水平最大值並將結果存儲在雙精度標量值中;如何在256位AVX矢量中查找水平最大值

我的嘗試都結束了使用了很多向量元素的混洗,使代碼不是非常優雅,也沒有效率。另外,我發現不可能只保留在AVX域名中。在某些時候,我不得不使用SSE 128位指令來提取最終的64位值。但是,我想在最後的聲明中被證明是錯誤的。

所以最理想的解決方案是:
1)只能使用AVX指令。 2)最小化指令的數量。 (我希望不超過3-4條指令)

話雖如此,任何優雅/高效的解決方案將被接受,即使它不符合上述指導原則。

感謝您的任何幫助。

-Luigi

+1

這是一個艱難的一...你只有1矢量這樣做呢?或者你有許多載體需要找到最大值?你可以(相當)有效地做4個並行的4×4向量轉置... – Mysticial 2012-03-20 22:28:55

+0

@Mysticial:嗯......我正在處理很多向量。然而,處理的簡單性並不能證明每次迭代都需要兩次4x4轉置操作。所以我正在處理一切「水平」而不需要換位。我以這種方式獲得了極大的提速,接近4倍,因爲我避免了轉置的開銷。一切都在手動展開4次的緊密循環中。但是,當循環結束時,我剩下最後一個AVX矢量。爲了將結果存回到我的雙精度標量值中,我必須找到其中最大的四個元素。因此,我的問題... – 2012-03-20 22:56:16

+0

如果它不在「緊密的循環」,它甚至性能至關重要? – Mysticial 2012-03-20 22:59:05

回答

12

我不認爲你能比4條指令做得更好:2次洗牌和2次比較。

__m256d x = ...; // input 

__m128d y = _mm256_extractf128_pd(x, 1); // extract x[2], and x[3] 
__m128d m1 = _mm_max_pd(x, y); // m1[0] = max(x[0], x[2]), m1[1] = max(x[1], x[3]) 
__m128d m2 = _mm_permute_pd(m1, 1); // set m2[0] = m1[1], m2[1] = m1[0] 
__m128d m = _mm_max_pd(m1, m2); // both m[0] and m[1] contain the horizontal max(x[0], x[1], x[2], x[3]) 

平凡的修改只有256位向量工作:

__m256d x = ...; // input 

__m256d y = _mm256_permute2f128_pd(x, x, 1); // permute 128-bit values 
__m256d m1 = _mm256_max_pd(x, y); // m1[0] = max(x[0], x[2]), m1[1] = max(x[1], x[3]), etc. 
__m256d m2 = _mm256_permute_pd(m1, 5); // set m2[0] = m1[1], m2[1] = m1[0], etc. 
__m256d m = _mm256_max_pd(m1, m2); // all m[0] ... m[3] contain the horizontal max(x[0], x[1], x[2], x[3]) 

(未經測試)

+0

是的,贊同...良好的解決方案。謝謝。 – 2012-03-21 08:41:16

2

這樣做了矢量v1 = [A, B, C, D]的一般方法是

  1. 置換v1v2 = [C, D, A, B](交換第0和第二元件,並且第一和第三的)
  2. 採取最大;即v3 = max(v1,v2)。你現在有[max(A,C), max(B,D), max(A,C), max(B,D)]
  3. 排列v3v4,交換第0和第1元素,第2和第3元素。
  4. 再次取最大值,即v5 = max(v3,v4)。現在,v5包含其所有組件中的水平最大值。

具體地爲AVX,所述置換可以用做_mm256_permute_pd和最大值可以用_mm256_max_pd來完成。我沒有準確的排列面具方便,但他們應該是相當簡單的弄清楚。

希望有所幫助。

+0

我特別喜歡你解決方案,因爲迄今爲止,它是唯一一個只使用AVX指令,而不會離開256位域的解決方案。謝謝。 – 2012-03-21 08:12:11

+0

對不起,我說了太早了......你不能用AVX做到這一點。大多數AVX操作不會跨越128位邊界。所以在這種情況下,你不能交換第0和第2元素以及第1和第3元素。 AVX排列操作只允許您更換第0和第1個元素或第2個和第3個元素。 – 2012-03-21 08:23:48

+0

@LuigiCastelli:我的解決方案可以寫,以便永遠不會離開256位的領域,如果你想。替換''由_mm256_permute2f128_pd _mm256_extractf128_pd'(X,X,1)',''由__m256d' __m128d'和'_mm _...''由_... _mm256','_mm_permute_pd(M1,1)通過''_mm256_permute_pd (m1,5)'。 – 2012-03-21 08:43:18

-1
//Use the code to find the horizontal maximum 
__m256 v1 = initial_vector;//example v1=[1 2 3 4 5 6 7 8] 
__m256 v2 = _mm256_permute_ps(v1,(int)147);//147 is control code for rotate left by upper 4 elements and lower 4 elements separately v2=[2 3 4 1 6 7 8 5] 
__m256 v3 = _mm256_max_ps(v1,v2);//v3=[2 3 4 4 6 7 8 8] 
__m256 v4 = _mm256_permute_ps(v3,(int)147);//v4=[3 4 4 2 7 8 8 6] 
__m256 v5 = _mm256_max_ps(v3,v4);//v5=[3 4 4 4 7 8 8 8] 
__m256 v6 = _mm256_permute_ps(v5,(int)147);//v6=[4 4 4 3 8 8 8 7] 
__m256 v7 = _mm256_max_ps(v5,v6);//contains max of upper four elements and lower 4 elements. v7=[4 4 4 4 8 8 8 8] 

//to get max of this horizontal array. Note that either upper or lower can contain the maximum 
float ALIGN max_array[8]; 
float horizontal_max; 
_mm256_store_ps(max_array, v7); 
if(max_array[0] > max_array[7]) 
{ 
    horizontal_max = max_array[0]; 
} 
else 
{ 
    horizontal_max = max_array[7]; 
} 
+1

對於浮點向量,它將需要一個額外的步驟,但存儲到一個數組並執行標量比較不是其中一個步驟。你仍然想從'extractf128'/128bit'maxps'開始。首先做內部的東西在Intel CPU上不會更好,對於AMD的CPU,256b AVX ops是128b AVX ops的兩倍,肯定會更糟。無論哪種方式,一個256b的商店,然後兩個負載 - >標量比較是愚蠢的,比'extractf128'慢。 – 2016-01-21 03:41:48