接下来再做一个测试,将并行和串行的循环次数设置为100,即将上例的main函数中:
for(int i = 0; i < 10000;i++)
更改为:
for(int i = 0; i < 100;i++)
然后分别运行10次,其结果如下表所示:
次数 | 串行 | 并行 |
1 | 0.00285003 | 0.007350087 |
2 | 0.00288031 | 0.00385478 |
3 | 0.0028603 | 0.00231841 |
4 | 0.00275407 | 0.0092762 |
5 | 0.00280949 | 0.00980167 |
6 | 0.00281462 | 0.00538499 |
7 | 0.00286081 | 0.00538858 |
8 | 0.00281103 | 0.00424682 |
9 | 0.00281309 | 0.00892829 |
10 | 0.00280026 | 0.00370494 |
平均值 | 0.002825401 | 0.006025477 |
从上面的分析结果可见,在循环100次的时候,只有极少数情况下并行计算比串行计算所需的时间要少,许多情况下并行需要更多的时间,这与我们设计并行程序的初衷是截然相反的。出现这种情况的原因之一就是,在分发并行的时候,系统也是需要消耗资源的,如果用于并行分发所耗的时间大于并行计算中节约的时间,那么这种情况下的并行计算就显得毫无意义,正如上例中的测试。所以,在并行计算中,并不是所有的并行计算都比串行计算要节约时间,具体要看并行的任务是否值得去做并行计算。还有一个重要的原因就是每次并行的线程数目,由于计算机CPU同时支持的并行线程有限,不可能指定并行100次, CPU在同一时刻就能运行100个线程。另外,由于这个比较程序的计算量非常小,很容易受到系统中其他因素的影响而导致计算结果的不稳定,所以,建议在进行测试时,尽量将计算量稍微调大一点,同时每次测试时应尽量保证运行环境相近,以减少系统中其他程序对测试结果的干扰。
下面用一个例子更能说明并行数目与程序运行效率间的关系,设置并形体中的计算量都一样,每次只是并行的次数不同,具体代码如下:
//File: Test02.cpp
#include "stdafx.h"
#include<omp.h>
#include<iostream>
using namespace std;
//循环测试函数
void test02()
{
for(inti=0;i<5000000;i++)
{
for(intj=0;j<1000;j++);
}
}
int main()
{
cout<<"这是一个新的并行测试程序!\n";
cout<<"请输入并行次数:\n";
intN=1;
cin>>N;
cout<<"开始进行计算...\n";
doublestart = omp_get_wtime( );//获取起始时间
#pragmaomp parallel for
for(inti = 0; i < N; i++)
{
test02();
}
doubleend = omp_get_wtime( );//获取结束时间
cout<<"计算耗时为:"<<end -start<<"\n";
cin>>end;
return0;
}
在近似的测试环境中分别对并行1次(相对于串行)、2次、3次等参数进行测试,其结果如下表:
N | 计算耗时(s) | N | 计算耗时(s) |
1 | 13.3731 | 12 | 27.526 |
2 | 13.343 | 13 | 28.9753 |
3 | 13.2072 | 14 | 28.0228 |
4 | 13.7742 | 15 | 29.4308 |
5 | 13.5256 | 16 | 28.5016 |
6 | 14.0692 | 17 | 41.1712 |
7 | 14.4321 | 18 | 41.3934 |
8 | 15.2443 | 24 | 47.6976 |
9 | 27.5141 | 25 | 55.7176 |
10 | 28.201 | 32 | 60.9475 |
11 | 29.0979 | 33 | 69.7519 |
计算时间与并行次数之间的关系如下图所示:
从以上分析结果可知,在并形体中计算量相同的情况下,在计算机CPU物理线程数目以内的并行次数可以得到更好的效果。这也可以很好的解释并行中线程数目设置的限制问题,如当设置9个并行线程时,由于计算机最多只支持8个线程,那第9个线程理所当然就不能和前面8个线程处于同一起跑线,它只能排在这8个线程的后面。假定第1到第8个线程是处于第一排,这一排的8个线程将会同时起步运行,而第9个到第16个处于第二排,它们则要在第一排后面起步,依次类推。每一时刻,都只会有8个线程在进行计算,而并行的结束是以所有的线程运行结束为标志,因此,最后那个线程(或者说最慢的那个线程)将决定并行结束的时间,所以线程越多,那么排队就越长,并行结束的时间相应就会增长。当然,具体一队中有多少个循环,可以通过schedule来设定,关于schedule后面将会详细介绍。
当然,上面的分析是基于并形体中计算量相同的前提,但有些情况下并形体的计算量与并行次数有着密切关系,譬如说总计算量一定的情况下,任何设置合理的并行次数对整个程序的运行起着至关重要的作用。如下例子,并行计算的总计算量一定,即如果设置并行次数越多,则并形体中的计算量越小。代码如下:
//File: Test03.cpp
#include "stdafx.h"
#include<omp.h>
#include<iostream>
using namespace std;
//循环测试函数
void test03(int space)
{
for(inti=0;i<space;i++)
{
for(intj=0;j<1000;j++);
}
}
int main()
{
inttotal=36288000;
cout<<"在总计算量一定的情况下测试程序!\n";
cout<<"请输入并行次数:\n";
intN=1;
cin>>N;
intspace =total/N;
cout<<"开始进行计算...\n";
doublestart = omp_get_wtime( );//获取起始时间
#pragmaomp parallel for
for(inti = 0; i < N; i++)
{
test03(space);
}
doubleend = omp_get_wtime( );//获取结束时间
cout<<"计算耗时为:"<<end -start<<"\n";
cin>>end;
return0;
}
分别设置并行次数从1到42进行测试,测试结果如下表所示:
N | 计算耗时 | N | 计算耗时 | N | 计算耗时 | N | 计算耗时 |
1 | 96.9828 | 14 | 14.3488 | 27 | 14.9022 | 40 | 12.8593 |
2 | 47.2697 | 15 | 13.7259 | 28 | 14.3302 | 41 | 14.7085 |
3 | 31.8575 | 16 | 12.8605 | 29 | 14.0329 | 42 | 14.3195 |
4 | 23.5635 | 17 | 17.213 | 30 | 13.6062 | 50 | 14.2404 |
5 | 19.2746 | 18 | 16.551 | 31 | 13.1146 | 60 | 13.5563 |
6 | 16.7957 | 19 | 15.819 | 32 | 12.8218 | 80 | 12.8094 |
7 | 14.2741 | 20 | 14.9696 | 33 | 15.0214 | 100 | 13.2439 |
8 | 12.8982 | 21 | 14.3318 | 34 | 14.7972 | 200 | 12.8838 |
9 | 21.3869 | 22 | 13.8786 | 35 | 14.4498 | 400 | 12.8444 |
10 | 19.7044 | 23 | 13.3461 | 36 | 14.0154 | 1000 | 12.8158 |
11 | 17.9848 | 24 | 12.9017 | 37 | 13.7218 | 2000 | 12.7411 |
12 | 16.4976 | 25 | 15.9088 | 38 | 13.3481 | ||
13 | 14.5436 | 26 | 15.5088 | 39 | 13.0619 |
由上图中可以很明显的看出,当并行数目达到计算机CPU最多线程数目时(测试计算机上CPU支持8线程),其计算效率将达到比较好的效果。假定计算机支持的最多线程为n,并行数目在0~n之间,计算效率逐渐增加,达到n时效率极高,当增加到n+1个并行数目时,计算效率会骤降,但在n+1与2n之间,计算效率同样也会逐渐增加,当达到2n+1时,又会骤降,随着并行数目不断增加,该规律将会不断的重现。
所以,综合以上两个测试例子也不难发现,并非并行数目越多,其计算效率就越高,具体效率跟程序结构以及计算机有着密切的联系。
相关程序源码下载地址:
http://download.csdn.net/detail/xwebsite/3843187