}
returnpi*4;
}doubleget_pi_sse(size_tdt){
doublepi=0.0;
doubledelta=1.0/dt;
__m128dxmm0,xmm1,xmm2,xmm3,xmm4;xmm0=_mm_set1_pd(1.0);
xmm1=_mm_set1_pd(delta);
xmm2=_mm_set_pd(delta,0.0);xmm4=_mm_setzero_pd();
for(longinti=0;i<=dt-2;i+=2){
xmm3=_mm_set1_pd((double)i*delta);
xmm3=_mm_add_pd(xmm3,xmm2);
xmm3=_mm_mul_pd(xmm3,xmm3);
xmm3=_mm_add_pd(xmm0,xmm3);
xmm3=_mm_div_pd(xmm1,xmm3);
xmm4=_mm_add_pd(xmm4,xmm3);
}--
doubletmp[2]__attribute__((aligned(16)));_mm_store_pd(tmp,xmm4);
pi+=tmp[0]+tmp[1]/*+tmp[2]+tmp[3]*/;
returnpi*4.0;}intmain()
{
intdx;
doublepai;
doublestart,finish;dx=N;
start=clock();pai=get_pi_sse(dx);
finish=clock();
printf("%.8lf\n",pai);
printf("%.8lfS\n",(double)((finish-start)/CLOCKS_PER_SEC));return0;
}
testl-SSE*c
时间运行如下:
adminl^admlnl-vlrtual-machine1«/Desktop$gcc-03adminl@adninl-virtual-machineDesktop$*/a-out
3,14159275
O.O08375O0Sadmtnl@adntnl-vtrtua'L-Fiachtnei~/Desktop$,/a.out3•14159275
0.0074110GS
adntAl@admini-virtuat-machine:
^/Desktop$»/a*out
3.14159275
O.OO82O80OS
adHtnlQadRtnl-vtrtual-Rdchine:
^/Desktops.M-out
3*14159275
O.fl67720OOS
第一次:
time=0.00837500S
第二次:
time=0.00741100S
第三次:
time=0.00772000S
三次平均为:
0.00783S
以下是SSE单精度的代码:
#include
#include
#include
#defineN10000000
floatget_pi_sse(size_tdt){
floatpi=0.0;
floatdelta=1.0/dt;
__m128xmm0,xmm1,xmm2,xmm3,xmm4;xmm0=_mm_set1_ps(1.0);
xmm1=_mm_set1_ps(delta);
xmm2=_mm_set_ps(delta*3,delta*2,delta,0.0);xmm4=_mm_setzero_ps();
for(longinti=0;i<=dt-4;i+=4){
xmm3=_mm_set1_ps((float)i*delta);
xmm3=_mm_add_ps(xmm3,xmm2);
xmm3=_mm_mul_ps(xmm3,xmm3);
xmm3=_mm_add_ps(xmm0,xmm3);
xmm3=_mm_div_ps(xmm1,xmm3);
xmm4=_mm_add_ps(xmm4,xmm3);
}--
floattmp[4]__attribute__((aligned(16)));
_mm_store_ps(tmp,xmm4);
pi+=tmp[0]+tmp[1]+tmp[2]+tmp[3];
returnpi*4.0;
}
intmain()
{
intdx;
floatpai;
doublestart,finish;
dx=N;
start=clock();
pai=get_pi_sse(dx);
finish=clock();
printf("%.8f\n",pai);
printf("%.8lfS\n",(double)((finish-start)/CLOCKS_PER_SEC));return0;
}
时间运行如下:
admtnl@adntnl-vtrtuaL-piachlne:
~/Desktopsgcc-03testl-floa七-SSE*Cadminl@adninl-vtrtual-i,iachine:
~/Desktop$./a*out
3.14564051
0*OO4O6100S
adnxnl@admini-virtual-machine:
-/Desktops-/a.out
B.14584651
O.OO426400S
admtnl®admtnl-vtrtiial-Fiachtne:
~/Deskt叩£*/a.out
3.145G4051
第一次:
time=0.00406100S
第二次:
time=0.00426400S
第三次:
time=0.00437600S
三次平均为:
0.00423S
1.1.3AVX向量优化版本设计
任务:
此部分需要给出单精度和双精度两个优化版本
注意:
(1)测试均在划分度为10的7次方下完成。
(2)在编译时需要加-mavx编译选项,才能启用AVX指令集,否则默认SSE指令集
(3)理论上,向量版本对比SSE版本和串行版本有明显加速,单精度版本速度明显优于双精度,速度接近双精度的两倍。
以下是AVX双精度的代码:
#include
#include
#include
#defineN10000000
/*doubleget_pi(intdt){
doublepi=0.0;
doubledelta=1.0/dt;
inti;
for(i=0;i
doublex=(double)i/dt;
pi+=delta/(1.0+x*x);
}
returnpi*4;
}*/
doubleget_pi_avx(size_tdt){
doublepi=0.0;
doubledelta=1.0/dt;
__m256dymm0,ymm1,ymm2,ymm3,ymm4;ymm0=_mm256_set1_pd(1.0);
ymm1=_mm256_set1_pd(delta);
ymm2=_mm256_set_pd(delta*3,delta*2,delta,0.0);
ymm4=_mm256_setzero_pd();
for(longinti=0;i<=dt-4;i+=4){
ymm3=_mm256_set1_pd((double)i*delta);
ymm3=_mm256_add_pd(ymm3,ymm2);
ymm3=_mm256_mul_pd(ymm3,ymm3);
ymm3=_mm256_add_pd(ymm0,ymm3);
ymm3=_mm256_div_pd(ymm1,ymm3);
ymm4=_mm256_add_pd(ymm4,ymm3);
}
doubletmp[4]__attribute__((aligned(32)));
_mm256_store_pd(tmp,ymm4);
pi+=tmp[0]+tmp[1]+tmp[2]+tmp[3];
returnpi*4.0;
}intmain()
{
intdx;
doublepai;
doublestart,finish;
dx=N;
start=clock();
pai=get_pi_avx(dx);
finish=clock();
printf("%.8lf\n",pai);
printf("%.8lfS\n",(double)((finish-start)/CLOCKS_PER_SEC));return0;
}
时间运行如下:
ad^tnl(aadntftl-virtual-Machtfte:
-/Oesktop$gcc-mavx*03testl-AVX.cadpitnlOadpitnl-vlrtuaL-machlne:
Desktop$*/a.out
3.14159275
adninlQadmini-virtual-machine:
-/Desktops/a.out
3.14159275
0.0065980GS
admtnl^admlnl-virtual-ma匚hlne:
«/Desktop$,/a.out
3.14159275
O.0O67O6QOS
第一次:
time=0.00720200S
第二次:
time=0.00659800S
第三次:
time=0.00670600S
三次平均为:
0.00683S
以下是AVX单精度的代码:
时间运行如下:
adnlnKaadfltnL-vtrtuaL'Wachtne:
/Desktopsgcc*navx-03testl-float-AVX^cadm/Desktops./a.out
3.1^1173317
0.002342035
adriLnl(3ad»i1.nL-v1.rtkjaL-machtne:
-/0esktop$*/a*out
3.14173317
□dntni^sdHini-virtual-nachine:
-/Dtsktop$,/d*out
3.14173317
U.OO230O&OS
第一次:
time=0.00234200S
第二次:
time=0.00234200S
第三次:
time=0.00230000S
三次平均为:
0.002328S
由以上实验统计得出结论:
AVX-float=0.002328S
AVX-double=0.00683S
SSE-float=0.00423S
SSE-double二0.00783S
基本符合规律:
(以下为速度比较)
AVX-float>AVX-double〜SSE-float>SSE-double>serial
1.2积分计算圆周率的OpenMP优化
1.2.1OpenMP并行化
任务:
在串行代码的基础上进行OpenMP并行优化
注意:
测试在划分度为10的9次方下完成。
参考代码:
#include
#include
#defineN1000000000
doubleget_pi(intdt){
doublepi=0.0;
doubledelta=1.0/dt;
inti;
#pragmaompparallelforreduction(+:
pi)for(i=0;i
doublex=(double)i/dt;pi+=delta/(1.0+x*x);
}
returnpi*4;
}
intmain()
{
intdx;
doublepai;
//doublestart,finish;
dx=N;
doublestart=omp_get_wtime();pai=get_pi(dx);
doublefinish=omp_get_wtime();
printf("%.8lf\n",pai);
printf("%lf\n",finish-start);
return0;
}
运行结果如下图:
**************************Machtne:
-/Desktopsgcc-fopenmp心testi^cadntnl^adntril-virtual-machtne:
-/Desktops./白*out
3.1415眈©5
1.Z54893
adninl@odrtiril'Virtual-Hachtne:
-/Desktops*»out
3.14159265
1.26064SadninHaadnifil-virtual-nachine:
-/Desktop$.fa*out
3.14159265
1.244998
串行结果如下:
ddntnl@adntnl-vtrtuaL-Fiachtne:
-/Desktopsgcc-03textileadmtnl@a-dmtnl-vlrtuaL-machlne:
-/Desktops■丿日•out
3.14159265
2.3297720OS
ddHtnl@admtnl-vtrtud'l-maGhtnQ:
-/Desktops・/a*out
3.14159265
2.334373005adninl@adntnl-vtrtual-nachilne:
-^Desktops./a.out
3.14159265
2.33470196S
提速十分明显
122OpenMP并行化+SIMD向量化
任务:
实现OpenMP线程级和SIMD两级并行
自动向量化代码如下:
#include
#include
#defineN1000000000
doubleget_pi(intdt){
doublepi=0.0;
doubledelta=1.0/dt;
inti;
#pragmaompparallelforsimdreduction(+:
pi)for(i=0;i
doublex=(double)i/dt;pi+=delta/(1.0+x*x);
}
returnpi*4;
}
intmain()
{
intdx;
doublepai;
dx=N;
doublestart=omp_get_wtime();
pai=get_pi(dx);
doublefinish=omp_get_wtime();
printf("%.8lf\n",pai);
printf("%lf\n",finish-start);return0;
}
注意:
自动向量化语句为#pragmaompparallelforsimdfor...
使用编译语句为:
gcc-fopenmp-mavx-O3...
运行结果如下图:
adn^nl@admtnl-vlrtual-nachlne;"/Desktop?
gcc-fopenfip-navx-03test2-si-nd*cadHtnlg^dmtnl-vtrtjaL-ri^chtne:
Desktop^./a*out
3.14159265
6,663686
adri'inltJadri'Lnl-virtual-nachine;-/Desktopg«/a.out
3.14159265
0.633444adninH0admi.nl-vtrtjal-fiachi.ne:
-/iDesktop5^/ri.out
3.14159205
0+640612
从结果看出:
有很明显的提速
手动向量化代码如下:
#include
#include
#include
#defineN1000000000doubleget_pi(intdt){
doublepi=0.0;
doubledelta=1.0/dt;
doubletmp[4]__attribute__((aligned(32)));
__m256dymm0,ymm1,ymm2,ymm3,ymm4;ymm0=_mm256_set1_pd(1.0);
ymm仁_mm256_set1_pd(delta);ymm2=_mm256_set_pd(delta*3,delta*2,delta,0.0);
ymm4=_mm256_setzero_pd();
inti;
#pragmaompparallelshared(ymm0,ymm1,ymm2)private(i,ymm3,tmp){
#pragmaompforreduction(+:
pi)
for(longinti=0;i<=dt-4;i+=4){
ymm3=_mm256_set1_pd((double)i*delta);
ymm3=_mm256_add_pd(ymm3,ymm2);
ymm3=_mm256_mul_pd(ymm3,ymm3);
ymm3=_mm256_add_pd(ymm0,ymm3);
ymm3=_mm256_div_pd(ymm1,ymm3);〃ymm4=_mm256_add_pd(ymm4,ymm3);
_mm256_store_pd(tmp,ymm3);
pi+=tmp[0]+tmp[1]+tmp[2]+tmp[3];
}
}
//doubletmp[4]__attribute__((aligned(32)));
〃_mm256_store_pd(tmp,ymm4);
〃pi+=tmp[0]+tmp[1]+tmp[2]+tmp[3];
returnpi*4.0;
}
intmain()
{
intdx;
doublepai;
dx=N;
doublestart=omp_get_wtime();
pai=get_pi(dx);
doublefinish=omp_get_wtime();
printf("%.8lf\n",pai);
printf("%lf\n",finish-start);
return0;
}
通过对向量化代码的分析,各个向量间的运算是没有任何依赖关系的,可以直接分线程并行运算,但需要注意最后要把各个线程的运算结果累加。
而线程的定义openmp的函数reduction是没有办法直接使用(+:
)进行累加,需要手动完成。
引入数组tmp用于将ymm3向量分割存放,并累加到pi变量,使用
openmp函数reduction(+:
pi)对pi变量进行累加(详见代码
解决的问题:
并行块中如何私有化一个数组:
直接将数组名称写入private()函
数中。
曾经尝试将数组各项都放入private。
函数中,错误如下:
adntnl®adntnl-vtrtual-machlne:
-/Desktop$gcc-fopennp-navx*05test2handxtestz-hand.c:
Infunctionfget_ptJ:
test2-hand.c:
16:
63:
_rexpected*)'before'[‘token
ffpragnacnpparallelshared(ynnerynnl#y)privateCt^ynRS,tnpQI,tnipri](tnp[2]>W[3])
多次尝试后,正确做法如下:
以tmp[4]数组举例:
私有化描述如下:
private(tmp);
运行结果如下图:
adntnl^ad-