Openmp嵌套循环
只是玩弄openmp。 看看这段代码片段:
#pragma omp parallel { for( i =0;i<n;i++) { doing something } }
和
for( i =0;i<n;i++) { #pragma omp parallel { doing something } }
为什么第一个比第二个慢得多(大约5倍)? 从理论上我认为第一个必须更快,因为并行区域只创建一次而不是像第二次那样n次? 谁可以给我解释一下这个?
我想要并行化的代码具有以下结构:
for(i=0;i<n;i++) //wont be parallelizable { for(j=i+1;j<n;j++) //will be parallelized { doing sth. } for(j=i+1;j<n;j++) //will be parallelized for(k = i+1;k<n;k++) { doing sth. } }
我做了一个简单的程序来测量时间并重现我的结果。
#include #include void test( int n) { int i ; double t_a = 0.0, t_b = 0.0 ; t_a = omp_get_wtime() ; #pragma omp parallel { for(i=0;i<n;i++) { } } t_b = omp_get_wtime() ; for(i=0;i<n;i++) { #pragma omp parallel { } } printf( "directive outside for-loop: %lf\n", 1000*(omp_get_wtime()-t_a)) ; printf( "directive inside for-loop: %lf \n", 1000*(omp_get_wtime()-t_b)) ; } int main(void) { int i, n ; double t_1 = 0.0, t_2 = 0.0 ; printf( "n: " ) ; scanf( "%d", &n ) ; t_1 = omp_get_wtime() ; #pragma omp parallel { for(i=0;i<n;i++) { } } t_2 = omp_get_wtime() ; for(i=0;i<n;i++) { #pragma omp parallel { } } printf( "directive outside for-loop: %lf\n", 1000*(omp_get_wtime()-t_1)) ; printf( "directive inside for-loop: %lf \n", 1000*(omp_get_wtime()-t_2)) ; test(n) ; return 0 ; }
如果我用不同的n开始,我总会得到不同的结果。
n: 30000 directive outside for-loop: 0.881884 directive inside for-loop: 0.073054 directive outside for-loop: 0.049098 directive inside for-loop: 0.011663 n: 30000 directive outside for-loop: 0.402774 directive inside for-loop: 0.071588 directive outside for-loop: 0.049168 directive inside for-loop: 0.012013 n: 30000 directive outside for-loop: 2.198740 directive inside for-loop: 0.065301 directive outside for-loop: 0.047911 directive inside for-loop: 0.012152 n: 1000 directive outside for-loop: 0.355841 directive inside for-loop: 0.079480 directive outside for-loop: 0.013549 directive inside for-loop: 0.012362 n: 10000 directive outside for-loop: 0.926234 directive inside for-loop: 0.071098 directive outside for-loop: 0.023536 directive inside for-loop: 0.012222 n: 10000 directive outside for-loop: 0.354025 directive inside for-loop: 0.073542 directive outside for-loop: 0.023607 directive inside for-loop: 0.012292
你怎么能解释我这个差异?!
您的版本结果:
Input n: 1000 [2] directive outside for-loop: 0.331396 [2] directive inside for-loop: 0.002864 [2] directive outside for-loop: 0.011663 [2] directive inside for-loop: 0.001188 [1] directive outside for-loop: 0.021092 [1] directive inside for-loop: 0.001327 [1] directive outside for-loop: 0.005238 [1] directive inside for-loop: 0.001048 [0] directive outside for-loop: 0.020812 [0] directive inside for-loop: 0.001188 [0] directive outside for-loop: 0.005029 [0] directive inside for-loop: 0.001257
因为并行区域只创建一次而不是像第二次那样的n次?
的种类。 那个工程
#pragma omp parallel { }
还意味着将工作项分配给'{‘上的线程并将线程返回到’}’上的线程池中。 它有很multithreading到线程的通信。 此外,默认情况下,等待线程将通过OS进入hibernate状态,并且唤醒线程需要一些时间。
关于你的中间样本:你可以试着用…来限制外部的并行性
#pragma omp parallel private(i,k) { for(i=0;i
通常,将嵌套的最高(上)可能性放置在嵌套上比将其放置在内环上更好。 如果需要顺序执行某些代码,请使用高级编译指示(如omp barrier
, omp master
或omp single
)或omp_locks作为此代码。 任何这种方式都会比多次omp parallel
启动omp parallel
更快
你的全面测试是非常错误的。 你确实计算了代码部分和第二部分的时间; 不是第一节的时间。 另外,printf的第二行确实测量了第一次printf的时间。
首次运行非常慢,因为这里有一个线程启动时间,内存初始化和缓存效果。 此外,omp的启发式可以在几个平行区域之后自动调整
我的测试版本:
$ cat test.c #include #include void test( int n, int j) { int i ; double t_a = 0.0, t_b = 0.0, t_c = 0.0 ; t_a = omp_get_wtime() ; #pragma omp parallel { for(i=0;i 0 ) { t_1 = omp_get_wtime(); #pragma omp parallel { for(i=0;i
我为程序内部的每个n做了3次运行。
结果:
$ ./test Input n: 1000 [2] directive outside for-loop: 5.044824 [2] directive inside for-loop: 48.605116 [2] directive outside for-loop: 0.115031 [2] directive inside for-loop: 1.469195 [1] directive outside for-loop: 0.082415 [1] directive inside for-loop: 1.455855 [1] directive outside for-loop: 0.081297 [1] directive inside for-loop: 1.462352 [0] directive outside for-loop: 0.080528 [0] directive inside for-loop: 1.455786 [0] directive outside for-loop: 0.080807 [0] directive inside for-loop: 1.467101
只有第一次运行test()
会受到影响。 test
和main()
所有下一个结果都是相同的。
从这样的运行中获得更好更稳定的结果(我使用gcc-4.6.1和静态构建)
$ OMP_WAIT_POLICY=active GOMP_CPU_AFFINITY=0-15 OMP_NUM_THREADS=2 ./test Input n: 5000 [2] directive outside for-loop: 0.079412 [2] directive inside for-loop: 4.266087 [2] directive outside for-loop: 0.031708 [2] directive inside for-loop: 4.319727 [1] directive outside for-loop: 0.047563 [1] directive inside for-loop: 4.290812 [1] directive outside for-loop: 0.033733 [1] directive inside for-loop: 4.324406 [0] directive outside for-loop: 0.047004 [0] directive inside for-loop: 4.273143 [0] directive outside for-loop: 0.092331 [0] directive inside for-loop: 4.279219
我确实将两个omp性能环境变量和有限的线程数设置为2。
也。 你“并行”循环是错误的。 (我在我的^^^变体中重现了这个错误)i变量在这里共享:
#pragma omp parallel { for(i=0;i
你应该把它作为
#pragma omp parallel { for(int local_i=0;local_i
UPDATE7你的结果是n = 1000:
[2] directive inside for-loop: 0.001188 [1] directive outside for-loop: 0.021092 [1] directive inside for-loop: 0.001327 [1] directive outside for-loop: 0.005238 [1] directive inside for-loop: 0.001048 [0] directive outside for-loop: 0.020812 [0] directive inside for-loop: 0.001188 [0] directive outside for-loop: 0.005029 [0] directive inside for-loop: 0.001257
代码的0.001或0.02输出是......秒乘以1000,所以它是毫秒(ms)。 它是......大约1微秒或20微秒。 某些系统时钟( user time
或time
效用的system time
输出字段)的粒度为1毫秒,3毫秒或10毫秒。 1微秒是2000-3000 CPU滴答(对于2-3GHz CPU)。 因此,如果没有特殊设置,您无法测量如此短的时间间隔。 你应该:
- 禁用CPU(Intel SpeedStep,AMD ???)的节能,可以通过降低时钟(频率)将CPU置于低功耗状态;
- 禁用CPU的动态超频(Intel turbostep);
- 在没有OS帮助的情况下测量时间,例如通过读取TSC(
rdtsc
asm指令) - 通过添加
cpuid
指令(或将禁用重新排序的其他指令)禁用rdtsc
之前和之后的rdtsc
CPU上的指令重新排序(只有当前一代的primefaces不是OOO cpu) - 在完全免费的系统上运行(在开始测试之前,两个cpu上的0%cpu负载)
- 以非交互方式重写测试(不要等待用户输入
scanf
,通过argv[1]
传递nargv[1]
) - 不要使用Xserver和慢速终端输出结果
- 使中断号码更低(关闭网络,物理;不要在后台播放电影,不要触摸鼠标和键盘)
- 做了很多次运行(我的意思是不是程序重启,而是重新启动程序的测量部分;我的程序中j = 100)并在结果上添加统计计算。
- 不要经常运行printf(在措施之间); 它会污染缓存和TLB。 在内部存储结果并在完成所有测量后输出结果。
更新8:统计我的意思是:取几个值,7个或更多。 丢弃第一个值(如果测量的数值很大,则丢弃第一个值)。 排序他们。 丢弃...最大和最小结果的10-20%。 计算平均值。 按照字面
double results[100], sum=0.0, mean = 0.0; int count = 0; // sort results[5]..results[100] here for(it=20; it< 85; it ++) { count++; sum+= results[it]; } mean = sum/count;