2012-03-20 57 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