OpenMP实验报告一、OpenMP实验第一部分parallelConstruct1.程序代码#includestdafx.h#includeomp.hintmain(intargc,char*argv[]){intid,numb;omp_set_num_threads(3);#pragmaompparallelprivate(id,numb){id=omp_get_thread_num();numb=omp_get_num_threads();printf(Iamthread%doutof%d\n,id,numb);}}2.运行结果截屏3.实验分析总结因为线程数等于3,所以三个线程并行执行,id变量获取线程编号,numb变量获取线程总数,所以输出语句printf(Iamthread%doutof%d\n,id,numb)输出线程号及线程总数。因为三个线程同时执行,所以三个线程中的输出语句顺序不一定。worksharingconstruct1.程序代码#includestdafx.h#includeomp.hintmain(intargc,char*argv[]){intid;omp_set_num_threads(2);#pragmaompparallel{printf(whatwillyousee?\n);#pragmaompforfor(inti=1;i=5;++i){printf(i=%dof%d\n,i,omp_get_thread_num());printf(Howmanyanswers?\n);}}return0;}2.运行结果截屏改变线程数和循环次数3.实验分析总结当程序设置两个线程时,在#pragmaompparallel指令中,程序应该并行执行,所以其后的输出语句printf(whatwillyousee?\n)并行输出两句;在#pragmaompfor指令中,程序将循环分配到两个线程中执行,实现线程间共享,线程0负责执行循环中的前部分循环,即i=1,2,3.线程1负责执行循环的后半部分,即i=4,5.由于两个线程还是并行执行,所以两个线程几乎同时执行相同的输出语句,先输出两句printf(i=%dof%d\n,i,omp_get_thread_num())语句,再输出两句printf(Howmanyanswers?\n)语句。两者综合的并行程序1.程序代码#includestdafx.h#includestdafx.h#includeiostream#includestdio.h#includeomp.hintmain(){omp_set_num_threads(3);#pragmaompparallelforfor(inti=0;i10;++i){std::coutistd::endl;}return0;};2.实验结果截图将线程数改为5:3.实验分析总结将parallelconstruct与worksharingconstruct两者结合,使进程间既能并行执行,又能共享执行循环语句。当编译器发现#pragmaompparallelfor后,自动将下面的for循环分成N份,(N为线程数),然后把每份指派给一个线程去执行,而且多线程之间为并行执行。在程序中,若将线程数设置为3,一共执行10个循环,则将10个循环分配给三个线程执行,其中线程0执行i=0,1,2,3四个循环,线程1执行i=4,5,6三个循环,线程2执行i=7,8,9三个循环,由于循环间并行执行,所以3个线程几乎同时执行std::couti语句,输出的先后顺序不一定,然后再几乎同时执行std::outstd::endle语句,即输出回车。所以从第一个截图可以看出,程序先输出三个数字047,分别为三个线程所分配的任务中的第一个循环i值,然后再输出三个回车。因为每个线程之间是并行执行,所以每次执行时打印出的顺序可能都是不一样的。二、OpenMP实验第二部分1.OpenMP代码#includestdafx.h#includeomp.h#includetime.hstaticlongnum_steps=10000000;doublestep;#defineNUM_THREADS2intmain(intargc,_TCHAR*argv[]){inti,id;doublex,pi,sum=0;doublestartTime=0.0,endTime=0.0;startTime=clock();step=1.0/(double)num_steps;omp_set_num_threads(NUM_THREADS);#pragmaompparallelprivate(x,i,id)reduction(+:sum){id=omp_get_thread_num();for(i=id+1;i=num_steps;i=i+NUM_THREADS){x=(i-0.5)*step;sum=sum+4.0/(1.0+x*x);}}endTime=clock();pi=sum*step;printf(time=%f\n,(endTime-startTime)/CLOCKS_PER_SEC);printf(%f\n,pi);return0;}2.运行结果截屏第一部分:线程数不变改变num_steps的值num_steps的值不变,改变线程数第二部分:去掉reduction(+:sum)第三部分:增加一个critical3.实验分析总结(1)从程序运行结果的第一部分截图可以看出,当程序中的语句段如下时:#pragmaompparallelprivate(x,i,id)reduction(+:sum){id=omp_get_thread_num();for(i=id+1;i=num_steps;i=i+NUM_THREADS){x=(i-0.5)*step;sum=sum+4.0/(1.0+x*x);}}程序运行结果为pi=3.141593,time=0.015000.程序使用了openMP为我们提供的归约(reduction),它的意思是告诉编译器:下面的for循环你要分成多个线程跑,但每个线程都要保存变量sum的拷贝,循环结束后,所有线程把自己的sum累加起来作为最后的输出。所以使用reduction(+:sum)之后,程序将下面的for循环分成2份,(线程数为2),然后把每份指派给一个线程去执行,每个线程都算出一个sum值,循环结束之后,两个线程sum值的和作为pi的值输出。程序输出结果正确。当增加问题规模,增大num_steps的值时,用时增加。增大num_steps的值为1000000000时,time=2.031000计算用时明显增加。这是由于增大了计算规模,计算量变得更大,所以需要消耗更多的时间。当问题规模不变,增加线程数时,计算用时会减少,但当线程数增大到一定值时,计算用时基本保持一个值不再变化或稍微有些增加。这是由于实验室的计算机是多核计算机,每个核都能独立支撑一个线程,所以它能够支持多个线程并行计算。适当的增加线程数可以减少计算的时间,但是当线程数太多的时候,线程是轮着在使用CPU资源,而且这个时候需要操作系统来管理这些线程,管理就会有开销,所以用的时间会稍微增加一些。(2)从程序运行结果的第二部分截图可以看出,当程序中的语句段如下时:#pragmaompparallelprivate(x,i,id)//reduction(+:sum){id=omp_get_thread_num();for(i=id+1;i=num_steps;i=i+NUM_THREADS){x=(i-0.5)*step;sum=sum+4.0/(1.0+x*x);}}程序运行结果为pi=1.571764,time=0.046000.计算出的pi值明显与3.1415926相差太大,运算结果错误。这是一个竞态条件(racecondition)的问题,该问题可表述为,当多个线程并行执行时,有可能多个线程同时对某变量进行了读写操作,从而导致不可预知的结果。程序中有可能两个线程同时对sum变量进行操作,当一个线程A执行sum=sum+4.0/(1.0+x*x)语句的同时,另一个线程B正好在更新sum,而此时A还在用旧的sum做累加,于是出现了错误,导致运算结果与正确结果相差很大。(3)从程序运行结果的第三部分截图可以看出,当程序中的语句段如下时:#pragmaompparallelprivate(x,i,id)//reduction(+:sum){id=omp_get_thread_num();for(i=id+1;i=num_steps;i=i+NUM_THREADS){x=(i-0.5)*step;#pragmaompcriticalsum=sum+4.0/(1.0+x*x);}}程序运行结果为pi=3.141593,time=1.265000.计算出的pi值结果正确,但所用的时间明显比(1)中大很多。在程序中for循环还是被自动分成两份来并行执行,但我们将sum=sum+4.0/(1.0+x*x)语句放在用#pragmaompcritical指令里面,它的意思是:各个线程还是并行执行for里面的语句,但当你们执行到critical里面时,要注意有没有其他线程正在里面执行,如果有的话,要等其他线程执行完再进去执行。这样就避免了racecondition问题,但显而易见,它的执行速度会变低,因为可能存在线程等待的情况。在程序中两个线程并行执行for里面的语句,当一个线程执行到critical时,要检测另一个线程是否正在执行critical里面的语句,如果没有才进去执行,这样就不会出现(2)中两个线程同时对sum变量进行改变的情况,所以运行结果正确,但是由于可能要等待,所以时间长很多。当线程数变多时,由于等待的可能性更大,所用的时间会更长。三、OpenMP实验第三部分1.程序代码#includestdafx.h#defineMPICH_SKIP_MPICXX#includempi.h#includestdio.h#includeomp.h#includetime.h#defineNUM_THREADS2staticlongnum_steps=100000000;voidmain(intargc,char*argv[]){inti_start,i_end,i,myid,numprocs,id;doublepi,mypi,x,step,sum=0.0;doublestartwtime=0.0,endwtime;MPI_Init(&argc,&argv);MPI_Comm_size(MPI_COMM_WORLD,&numprocs);MPI_Comm_rank(MPI_COMM_WORLD,&myid);MPI_Bcast(&num_steps,1,MPI_INT,0,MPI_COMM_WORLD);i_start=myid+(num_steps/numprocs);i_end=i_start+(num_steps/numprocs);step=1.0/(double)num_steps;startwtime=MPI_Wtime();//时间记录omp_set_num_threads(NUM_THREADS);#pragmaompparallelprivate(x,i,id)reduction(+:sum){id=omp_get_thread_num();for(i=id+1;i=num_steps;i=i+NUM_THREADS){x=(i-0.5)*step;sum=sum+4.0/(1.0+x*x);}}mypi=step*sum/numprocs;MPI_Reduce(&mypi,&pi,1,MPI_DOUBLE,MPI_SUM,0,MPI_COMM_WORLD);endwtime=MPI_Wtime();if(myid==0)print