使用sse和avx内部函数将一组打包的单值添加到一个值中
Using sse and avx intrinsics to add a set of packed singles into one value
我有代码,我正在努力加快。首先,我使用了SSE特性,并看到了显著的收益。我现在正试图看看我是否可以做类似的与AVX的内在。从本质上讲,该代码取两个数组,根据需要加减它们,将结果平方,然后将所有这些平方相加。
下面是使用sse内部函数的简化版本:
float chiList[4] __attribute__((aligned(16)));
float chi = 0.0;
__m128 res;
__m128 nres;
__m128 del;
__m128 chiInter2;
__m128 chiInter;
while(runNum<boundary)
{
chiInter = _mm_setzero_ps();
for(int i=0; i<maxPts; i+=4)
{
//load the first batch of residuals and deltas
res = _mm_load_ps(resids+i);
del = _mm_load_ps(residDeltas[param]+i);
//subtract them
nres = _mm_sub_ps(res,del);
//load them back into memory
_mm_store_ps(resids+i,nres);
//square them and add them back to chi with the fused
//multiply and add instructions
chiInter = _mm_fmadd_ps(nres, nres, chiInter);
}
//add the 4 intermediate this way because testing
//shows it is faster than the commented out way below
//so chiInter2 has chiInter reversed
chiInter2 = _mm_shuffle_ps(chiInter,chiInter,_MM_SHUFFLE(0,1,2,3));
//add the two
_mm_store_ps(chiList,_mm_add_ps(chiInter,chiInter2));
//add again
chi=chiList[0]+chiList[1];
//now do stuff with the chi^2
//alternatively, the slow way
//_mm_store_ps(chiList,chiInter);
//chi=chiList[0]+chiList[1]+chiList[2]+chiList[3];
}
这让我想到了我的第一个问题:是否有任何方法可以更优雅地完成最后一位(我在chiInter中使用4个浮点数并将它们求和为一个浮点数)?
无论如何,我现在正试图使用avx内部函数来实现这一点,这个过程的大部分是相当简单的,不幸的是,我正在拖延试图做最后一点,试图将8个中间chi值压缩成一个值。
下面是avx内部函数的一段简化代码:float chiList[8] __attribute__((aligned(32)));
__m256 res;
__m256 del;
__m256 nres;
__m256 chiInter;
while(runNum<boundary)
{
chiInter = _mm256_setzero_ps();
for(int i=0; i<maxPts; i+=8)
{
//load the first batch of residuals and deltas
res = _mm256_load_ps(resids+i);
del = _mm256_load_ps(residDeltas[param]+i);
//subtract them
nres = _mm256_sub_ps(res,del);
//load them back into memory
_mm256_store_ps(resids+i,nres);
//square them and add them back to chi with the fused
//multiply and add instructions
chiInter = _mm256_fmadd_ps(nres, nres, chiInter);
}
_mm256_store_ps(chiList,chiInter);
chi=chiList[0]+chiList[1]+chiList[2]+chiList[3]+
chiList[4]+chiList[5]+chiList[6]+chiList[7];
}
我的第二个问题是:是否有一些方法像我上面拉的SSE那样,让我更快地完成这个最后的加法?或者,如果有更好的方法来做我在SSE intrinsic中所做的事情,它是否有与AVX intrinsic等效的方法?
这个操作叫做水平求和。假设你有一个向量v={x0,x1,x2,x3,x4,x5,x6,x7}
。首先,提取高/低部分,这样你就有了w1={x0,x1,x2,x3}
和w2={x4,x5,x6,x7}
。现在调用_mm_hadd_ps(w1, w2)
,得到:tmp1={x0+x1,x2+x3,x4+x5,x6+x7}
。同样,_mm_hadd_ps(tmp1,tmp1)
得到tmp2={x0+x1+x2+x3,x4+x5+x6+x7,...}
。最后一次,_mm_hadd_ps(tmp2,tmp2)
给出tmp3={x0+x1+x2+x3+x4+x5+x6+x7,...}
。您也可以将第一个_mm_hadd_ps
替换为一个简单的_mm_add_ps
。
这些都是未经测试的,并且是从文档中编写的。也不保证速度…
有人在英特尔论坛上展示了另一个变体(寻找HsumAvxFlt
)。
我们还可以通过编译gcc test.c -Ofast -mavx2 -S
float f(float*t){
t=(float*)__builtin_assume_aligned(t,32);
float r=0;
for(int i=0;i<8;i++)
r+=t[i];
return r;
}
生成的test.s
包含:
vhaddps %ymm0, %ymm0, %ymm0
vhaddps %ymm0, %ymm0, %ymm1
vperm2f128 $1, %ymm1, %ymm1, %ymm0
vaddps %ymm1, %ymm0, %ymm0
我有点惊讶最后一个指令不是vaddss
,但我想这并不重要。
- 为什么我必须在C++中添加一个赋值符号来声明一个数组
- 在多维向量中的特定位置添加一个值
- 向字符串添加位值
- C++ 特征:为什么可以为方法添加赋值,但不能为方法添加 Diagonal()
- 在 Arduino 中单击时将值添加到数组
- 如何在qt qmessagebox中添加可变值
- C++单值枚举与预处理器指令
- 向向下转换结构添加属性值会导致分段错误
- C++ 如何执行添加新值并可以删除现有值的循环
- 添加初始值设定项列表的元素
- 向字符串数组中的元素添加数字值
- 在程序选项中添加键值对
- 向CMFCPropertyGridCtrl添加/获取值
- 如何在菜单栏中添加单选按钮,qt 5 c++
- 添加长值显示不同的o/p
- 我如何在链表的前面添加一个值
- 向整型数组添加新值
- boost::transform() 仅当添加的值不是 NULL 时?
- 使用sse和avx内部函数将一组打包的单值添加到一个值中
- RCpp:期望出现单值错误