本文主要为大家介绍AVX2指令集优化浮点组求和算法。有需要的朋友可以借鉴一下,希望能有所帮助。祝大家进步很大,早日升职加薪。
目录
I av x2指令集简介描述运算性能II。代码实现0。数据生成1。通用阵列求和2。AVX2指令集求和:单精度浮点(float)3。AVX2指令集求和:双精度浮点(double) III。性能测试环境,计时方法,测试内容,性能测试,第一次测试,第二次测试四。摘要
一、AVX2指令集介绍
AVX2是SIMD(single instruction multi data,单指令多数据)指令集,支持在一个指令周期内同时操作256位存储器。包括乘法、加法、位运算等功能。下面附上的是英特尔官方网站用户文档。
英特尔内部指南
这次需要用到的指令有_ _ M256I _ MM 256 _ Add _ PD (_ _ M256IA,_ _ M256IB),_ _ M256I _ MM 256 _ Add _ PS等。(P代表精度,S代表单精度,D代表双精度)
它们可以一次获取256位内存,并以浮点形式增加32/64位。下面附上官网说明。
Synopsis
__m256d _mm256_add_pd (__m256d a,__m256d b)
#包含immintrin.h
指令:vaddpd ymm,ymm,ymm
CPUID标志:AVX
Description
将a和b中打包的双精度(64位)浮点元素相加,结果存储在dst中。
Operation
对于j :=0到3
i :=j*64
dst[i 63:i] :=a[i 63:i] b[i 63:i]
结束
dst[最大值:256] :=0
Performance
建筑吞吐量(CPI)冰湖
二、代码实现
0. 数据生成
为了比较结果,我们生成从1到n的算术级数。这里,模板用于兼容不同的数据类型。由于AVX2指令集一次要操作多个数据,为了防止内存访问越界,我们将大小扩展到256整数倍位,即32字节的整数倍。
uint64_t低位(uint64_t x)
{
返回x(-x);
}
uint64_t extTo2Power(uint64_t n,int i)//arraysize数据大小
{
while(低位(n) i)
n=low bit(n);
返回n;
}
模板类型名T
T* getArray(uint64_t大小)
{
uint 64 _ T ExSize=ext to 2 power(size,32/sizeof(T));
T * arr=new T[ExSize];
for(uint 64 _ t I=0;I尺寸;我)
arr[I]=I ^ 1;
for(uint 64 _ t I=size;我ExSize我)
arr[I]=0;
返回arr
}
1. 普通数组求和
为了比较性能差异,我们首先实现一个普通的数组和。这里也用到了模板。
模板类型名T
T simpleSum(T* arr,uint64_t size)
{
t sum=0;
for(uint 64 _ t I=0;I尺寸;我)
sum=arr[I];
返回总和;
}
2. AVX2指令集求和:单精度浮点(float)
这里我们预打开avx2的一个整形变量,一次从数组中取出8个32位浮点,加到这个变量中,最后将这8个32位浮点求和。
float avx2Sum(float* arr,uint64_t size)
{
float sum[8]={ 0 };
_ _ m256 sum 256=_ mm 256 _ set zero _ PS();
_ _ m256 load 256=_ mm 256 _ set zero _ PS();
for(uint 64 _ t I=0;I尺寸;i=8)
{
load 256=_ mm 256 _ loadu _ PS(arr[I]);
sum256=_mm256_add_ps(sum256,load 256);
}
sum256=_mm256_hadd_ps(sum256,sum 256);
sum256=_mm256_hadd_ps(sum256,sum 256);
_mm256_storeu_ps(sum,sum 256);
总和[0]=总和[4];
返回sum[0];
}
这里的Hadd是水平加法,其具体实现类似于下图,可以帮助我们实现数组内的求和:
3. AVX2指令集求和:双精度浮点(double)
double avx2Sum(double* arr,uint64_t size)
{
double sum[4]={ 0 };
_ _ m256d sum 256=_ mm 256 _ set zero _ PD();
_ _ m256d load 256=_ mm 256 _ set zero _ PD();
for(uint 64 _ t I=0;I尺寸;i=4)
{
load 256=_ mm 256 _ loadu _ PD(arr[I]);
sum256=_mm256_add_pd(sum256,load 256);
}
sum256=_mm256_hadd_pd(sum256,sum 256);
_mm256_storeu_pd(sum,sum 256);
总和[0]=总和[2];
返回sum[0];
}
三、性能测试
测试环境
设备描述英特尔酷睿i9-9880H 8核2.3 GHz内存DDR4-2400MHz双通道32 gbc编译器Clang-1300.0.29.30
计时方式
利用chrono库获取系统时钟,计算运行时间,精确到毫秒级。
uint64_t getTime()
{
uint 64 _ t time ms=STD:chrono:duration _ cast STD:chrono:毫秒(std:chrono:system_clock:now()。time_since_epoch())。count();
返回时间ms;
}
测试内容
求和1到1e9,答案应该是50000000050000000。分别测试float和double。
uint64 _ t N=1e9
//比较普通加法和avx2加法的性能
uint64_t开始,结束;
//测试浮点
cout ' compare float sum:' endl;
float * arr 3=getArrayfloat(N);
start=getTime();
float sum3=simpleSum(arr3,N);
end=getTime();
cout ' float simple sum time:' end-start endl;
cout ' float simple sum sum:' sum 3 endl;
start=getTime();
sum3=avx2Sum(arr3,N);
end=getTime();
cout ' float avx 2 sum time:' end-start endl;
cout ' float avx 2 sum sum:' sum 3 endl;
删除[]arr 3;
cout endl endl
//测试双精度
cout ' compare double sum:' endl;
double * arr 4=getArraydouble(N);
start=getTime();
double sum4=simpleSum(arr4,N);
end=getTime();
cout ' double simple sum time:' end-start endl;
cout ' double simple sum sum:' sum 4 endl;
start=getTime();
sum4=avx2Sum(arr4,N);
end=getTime();
cout ' double avx 2 sum time:' end-start endl;
cout ' double avx 2 sum sum:' sum 4 endl;
删除[]arr 4;
cout endl endl
进行性能测试
第一次测试
测试命令
g -mavx2 avx_big_integer.cpp。/a.out
试验结果
方法耗时(ms)AVX2加法单精度615普通加法单精度2229AVX2加法双精度1237普通加法双精度2426
这里可以看到,在单精度下出现了明显的误差,并且由于普通求和与avx2求和的加法顺序不同,误差值也不同。
第二次测试
测试命令
现在让我们打开O2编译优化,试一试:
g -O2 -mavx2 avx_big_integer.cpp。/a.out
试验结果
方法耗时(ms)AVX2加法32位244普通加法32位1012AVX2加法64位476普通加法64位1292
我们发现,相比上次测试整形,开启O2优化后,通过AVX2指令集添加,浮点型有了明显的提升。
四、总结
可以看出,在进行浮点运算时,用avx2指令集进行并行优化可以得到比整形更好的结果。
个人猜测原因:
浮点加法器比整形加法器复杂得多,流水线操作的效果也没有那么明显。有可能CPU中的浮点加法器比整形加法器少,导致O2优化在乱序执行时优化效果不如整形理想。AVX2指令集可能专门针对浮点运算进行了优化,使得浮点运算的性能更接近整形运算。
以上是AVX2指令集优化浮点组求和算法的详细内容。有关AVX2指令集浮点组求和的更多信息,请关注我们的其他相关文章!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。