确定 __m256 值的跨 SIMD 通道的最小值
Determine the minimum across SIMD lanes of __m256 value
我了解通常应避免跨 SIMD 通道的操作。
然而,有时不得不这样做。
我正在使用 AVX2 内在函数,并且在 __m256.
中有 8 个浮点值
我想知道这个向量中的最低值,更复杂的是:还有这是在哪个插槽中。
我目前的解决方案是内存往返,我不喜欢这样:
float closestvals[8];
_mm256_store_ps( closestvals, closest8 );
float closest = closestvals[0];
int closestidx = 0;
for ( int k=1; k<8; ++k )
{
if ( closestvals[k] < closest )
{
closest = closestvals[ k ];
closestidx = k;
}
}
在不占用 to/from 内存的情况下执行此操作的好方法是什么?
你可以试试这个:
#include <stdio.h>
#include <x86intrin.h>
#include <math.h>
/* gcc -O3 -Wall -m64 -march=haswell hor_min.c */
int print_vec_ps(__m256 x);
int main() {
float x[8]={1.2f, 3.6f, 2.1f, 9.4f, 4.0f, 0.1f, 8.9f, 3.3f};
/* Note that the results are not useful if one of the inputs is a 'not a number'. The input below leads to indx = 32 (!) */
// float x[8]={1.2f, 3.6f, 2.1f, NAN, 4.0f, 2.0f , 8.9f, 3.3f};
__m256 v0 = _mm256_load_ps(x); /* _mm256_shuffle_ps instead of _mm256_permute_ps is also possible, see Peter Cordes' comments */
__m256 v1 = _mm256_permute_ps(v0,0b10110001); /* swap floats: 0<->1, 2<->3, 4<->5, 6<->7 */
__m256 v2 = _mm256_min_ps(v0,v1);
__m256 v3 = _mm256_permute_ps(v2,0b01001110); /* swap floats */
__m256 v4 = _mm256_min_ps(v2,v3);
__m256 v5 = _mm256_castpd_ps(_mm256_permute4x64_pd(_mm256_castps_pd(v4),0b01001110)); /* swap 128-bit lanes */
__m256 v_min = _mm256_min_ps(v4,v5);
__m256 mask = _mm256_cmp_ps(v0,v_min,0);
int indx = _tzcnt_u32(_mm256_movemask_ps(mask));
printf(" 7 6 5 4 3 2 1 0\n");
printf("v0 = ");print_vec_ps(v0 );
printf("v1 = ");print_vec_ps(v1 );
printf("v2 = ");print_vec_ps(v2 );
printf("\nv3 = ");print_vec_ps(v3 );
printf("v4 = ");print_vec_ps(v4 );
printf("\nv5 = ");print_vec_ps(v5 );
printf("v_min = ");print_vec_ps(v_min );
printf("mask = ");print_vec_ps(mask );
printf("indx = ");printf("%d\n",indx);
return 0;
}
int print_vec_ps(__m256 x){
float v[8];
_mm256_storeu_ps(v,x);
printf("%5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f\n",
v[7],v[6],v[5],v[4],v[3],v[2],v[1],v[0]);
return 0;
}
输出:
./a.out
7 6 5 4 3 2 1 0
v0 = 3.30 8.90 0.10 4.00 9.40 2.10 3.60 1.20
v1 = 8.90 3.30 4.00 0.10 2.10 9.40 1.20 3.60
v2 = 3.30 3.30 0.10 0.10 2.10 2.10 1.20 1.20
v3 = 0.10 0.10 3.30 3.30 1.20 1.20 2.10 2.10
v4 = 0.10 0.10 0.10 0.10 1.20 1.20 1.20 1.20
v5 = 1.20 1.20 1.20 1.20 0.10 0.10 0.10 0.10
v_min = 0.10 0.10 0.10 0.10 0.10 0.10 0.10 0.10
mask = 0.00 0.00 -nan 0.00 0.00 0.00 0.00 0.00
indx = 5
在此答案的先前版本中,128 位通道与 _mm256_permute2f128_ps
交换。
在此更新的答案中 _mm256_permute2f128_ps
被 _mm256_permute4x64_pd
取代,
这在 AMD CPU 和 Intel KNL 上更快,请参阅@Peter Cordes 的评论。
但请注意 _mm256_permute4x64_pd
需要 AVX2,而 AVX 足以满足 _mm256_permute2f128_ps
.
另请注意,如果其中一个输入值是 'not a number' (NAN),则此代码的结果将无用。
我了解通常应避免跨 SIMD 通道的操作。 然而,有时不得不这样做。
我正在使用 AVX2 内在函数,并且在 __m256.
中有 8 个浮点值我想知道这个向量中的最低值,更复杂的是:还有这是在哪个插槽中。
我目前的解决方案是内存往返,我不喜欢这样:
float closestvals[8];
_mm256_store_ps( closestvals, closest8 );
float closest = closestvals[0];
int closestidx = 0;
for ( int k=1; k<8; ++k )
{
if ( closestvals[k] < closest )
{
closest = closestvals[ k ];
closestidx = k;
}
}
在不占用 to/from 内存的情况下执行此操作的好方法是什么?
你可以试试这个:
#include <stdio.h>
#include <x86intrin.h>
#include <math.h>
/* gcc -O3 -Wall -m64 -march=haswell hor_min.c */
int print_vec_ps(__m256 x);
int main() {
float x[8]={1.2f, 3.6f, 2.1f, 9.4f, 4.0f, 0.1f, 8.9f, 3.3f};
/* Note that the results are not useful if one of the inputs is a 'not a number'. The input below leads to indx = 32 (!) */
// float x[8]={1.2f, 3.6f, 2.1f, NAN, 4.0f, 2.0f , 8.9f, 3.3f};
__m256 v0 = _mm256_load_ps(x); /* _mm256_shuffle_ps instead of _mm256_permute_ps is also possible, see Peter Cordes' comments */
__m256 v1 = _mm256_permute_ps(v0,0b10110001); /* swap floats: 0<->1, 2<->3, 4<->5, 6<->7 */
__m256 v2 = _mm256_min_ps(v0,v1);
__m256 v3 = _mm256_permute_ps(v2,0b01001110); /* swap floats */
__m256 v4 = _mm256_min_ps(v2,v3);
__m256 v5 = _mm256_castpd_ps(_mm256_permute4x64_pd(_mm256_castps_pd(v4),0b01001110)); /* swap 128-bit lanes */
__m256 v_min = _mm256_min_ps(v4,v5);
__m256 mask = _mm256_cmp_ps(v0,v_min,0);
int indx = _tzcnt_u32(_mm256_movemask_ps(mask));
printf(" 7 6 5 4 3 2 1 0\n");
printf("v0 = ");print_vec_ps(v0 );
printf("v1 = ");print_vec_ps(v1 );
printf("v2 = ");print_vec_ps(v2 );
printf("\nv3 = ");print_vec_ps(v3 );
printf("v4 = ");print_vec_ps(v4 );
printf("\nv5 = ");print_vec_ps(v5 );
printf("v_min = ");print_vec_ps(v_min );
printf("mask = ");print_vec_ps(mask );
printf("indx = ");printf("%d\n",indx);
return 0;
}
int print_vec_ps(__m256 x){
float v[8];
_mm256_storeu_ps(v,x);
printf("%5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f %5.2f\n",
v[7],v[6],v[5],v[4],v[3],v[2],v[1],v[0]);
return 0;
}
输出:
./a.out
7 6 5 4 3 2 1 0
v0 = 3.30 8.90 0.10 4.00 9.40 2.10 3.60 1.20
v1 = 8.90 3.30 4.00 0.10 2.10 9.40 1.20 3.60
v2 = 3.30 3.30 0.10 0.10 2.10 2.10 1.20 1.20
v3 = 0.10 0.10 3.30 3.30 1.20 1.20 2.10 2.10
v4 = 0.10 0.10 0.10 0.10 1.20 1.20 1.20 1.20
v5 = 1.20 1.20 1.20 1.20 0.10 0.10 0.10 0.10
v_min = 0.10 0.10 0.10 0.10 0.10 0.10 0.10 0.10
mask = 0.00 0.00 -nan 0.00 0.00 0.00 0.00 0.00
indx = 5
在此答案的先前版本中,128 位通道与 _mm256_permute2f128_ps
交换。
在此更新的答案中 _mm256_permute2f128_ps
被 _mm256_permute4x64_pd
取代,
这在 AMD CPU 和 Intel KNL 上更快,请参阅@Peter Cordes 的评论。
但请注意 _mm256_permute4x64_pd
需要 AVX2,而 AVX 足以满足 _mm256_permute2f128_ps
.
另请注意,如果其中一个输入值是 'not a number' (NAN),则此代码的结果将无用。