最近在研究OpenMp,写了一段代码,如下:

#include<time.h>
#include<stdio.h>
#include<stdlib.h>
#include<omp.h> #define THREAD_NUM 8
int main()
{
clock_t start,finish;
int n=; int sum;
start=clock();
for(int i=;i<n;i++)
{
sum+=;
sum-=;
} finish=clock();
printf("Serial computation\n");
printf("time:%lf\n",(double)(finish-start)/CLOCKS_PER_SEC); printf("Parallel computation\n");
start=clock();
#pragma omp parallel num_threads(THREAD_NUM)
{
int nth=omp_get_num_threads();
int me=omp_get_thread_num();
int mysum=;
clock_t t1,t2; t1=clock();
for(int i=me;i<n;i+=nth)
{
mysum+=;
mysum-=;
}
t2=clock();
printf("time:%lf\t%d\n",(double)(t2-t1)/CLOCKS_PER_SEC,mysum);
} finish=clock();
printf("Total time:%lf\n",(double)(finish-start)/CLOCKS_PER_SEC); return ;
}

输出结果:

Serial computation
time:0.356796
Parallel computation
time:0.154885
time:0.221016
time:0.284257
time:0.253218
time:0.296142
time:0.269889
time:0.312325
time:0.275955
Total time:0.322763

上面的结果很奇怪,程序开了8个线程,可是得到的结果却不是1/8,考虑到线程的创建等等开销,提升的幅度达不到8倍,但是也不至于就1.1倍左右啊;而且每个线程只做了计算的1/8次迭代,消耗的时间远远大于1/8的时间。

思考一下可能存在以下原因:

1) 线程中的printf这种函数并不是并行安全的,所以各个线程在最后快要结束的时候会争抢控制台资源,不过占用不了太多时间。

2) 线程的创建和撤销存在一定的消耗,不过个人觉得这部分也不会占用太多时间,如果这个结论成立,那么增加线程的计算时间,是不是可以提升幅度呢?

3) false sharing(参见我的上一篇博文),目测并不是false sharing的原因。

4) 存在其他冲突的资源,导致了线程之间存在关联,并不能完全并行。

5) 代码中的for循环在执行时的问题。

对上面的几点疑问,逐个进行了探讨。


线程的创建核撤销的消耗

增加线程的计算时间,那么提升的幅度会不会增加呢?考虑到此,做了如下的实验,将代码中的n改成160000000,那么得到的运行结果如下:

Serial computation
time:0.640447
Parallel computation
time:0.319365
time:0.503179
time:0.579748
time:0.581418
time:0.629072
time:0.592573
time:0.634568
time:0.609349
Total time:0.646393

这次的效果更糟糕,而且总的并行时间是比串行的还要慢,再看看单个线程的时间,虽然计算了1/8的迭代,可是时间除了第一个线程使用原先1/2时间外,剩下的几乎等于串行的时间。从实验的结果上来看,增加一倍迭代次数后,单个线程消耗的时间大致也会提高一倍,因此线程的创建和撤销的因素基本可以忽略。一定是某个原因导致了计算时间的快慢。


false sharing

程序代码中并行的部分全是私有化的变量,甚至都没有将mysum累加到主线程中,不会发生false sharing,这一点可以排除。


for循环

for循环会不会出现猫腻呢?为此也做了以下的实验:

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
int main()
{
int n=;
int sum=;
clock_t start,finish;
start=clock();
for(int i=;i<n;i++)
for(int j=;j<n;j++)
{
sum++;
sum--;
}
finish=clock();
printf("time1:%lf\n",(double)(finish-start)/CLOCKS_PER_SEC); start=clock();
for(int i=;i<n/;i++)
for(int j=;j<n*;j++)
{
sum++;
sum--;
}
finish=clock();
printf("time2:%lf\n",(double)(finish-start)/CLOCKS_PER_SEC); start=clock();
for(int i=;i<n*;i++)
for(int j=;j<n/;j++)
{
sum++;
sum--;
}
finish=clock();
printf("time3:%lf\n",(double)(finish-start)/CLOCKS_PER_SEC); start=clock();
for(int j=;j<n*n;j++)
{
sum++;
sum--;
}
finish=clock();
printf("time4:%lf\n",(double)(finish-start)/CLOCKS_PER_SEC); }

输出结果:

time1:0.431033
time2:0.377387
time3:0.383699
time4:0.372852

结果是循环相同的次数,单层是最快的,而外层和里层次数一样是最慢的,因为CPU 跨切循环层。

另外插一个题外话,for循环遍历不当也会引起false sharing,我们看下面的例子:

右边的循环之所以比左边的效率高,与程序访问的局部性和Cache命中率有关。数组在计算机中是行优先存储的,左边的循环中,依次访问的是变量a[0][0],a[1][0],a[2][0],...,a[99][0],a[0][1],a[1][1],a[2][1],……,a[99][1],……这实际上是按照列优先的原则在访问数组元素。如果Cache容量相对于数组容量而言不够大,考虑一个极端情况,假设Cache只有一个块,只能存储一行数据,则每访问一个元素就会发生一次Cache失效,就需要访问一次主存,读入一块数据,导致存储系统效率低下,明显影响操作延迟。而右边的循环采用的是行优先访问原则,与元素存储顺序一致。基于同样的假设,此时只有访问新一行的第一个数据时才发生Cache失效,通过访问主存读入一块连续的数据(恰为数组的一行),此后访问同行数据便可直接使用Cache中缓存的数据,直到访问下一行的第一个数据。Cache失效率降低了,整个存储系统的平均访问延迟降低了,显然程序执行效率较高。

言归正传,从for循环的实验中可以看出并不是计算时间的问题。


以上的几个方面都做了实验,都找不到问题所在,研究了几天了,姑且放在这吧。知道问题所在的,往告知,不甚感谢~~

OpenMp并行提升时间为什么不是线性的?的更多相关文章

  1. OpenMP并行编程应用—加速OpenCV图像拼接算法

    OpenMP是一种应用于多处理器程序设计的并行编程处理方案,它提供了对于并行编程的高层抽象.仅仅须要在程序中加入简单的指令,就能够编写高效的并行程序,而不用关心详细的并行实现细节.减少了并行编程的难度 ...

  2. OpenMP并行编程

    什么是OpenMP?“OpenMP (Open Multi-Processing) is an application programming interface (API) that support ...

  3. OpenMP并行程序设计——for循环并行化详解

    在C/C++中使用OpenMP优化代码方便又简单,代码中需要并行处理的往往是一些比较耗时的for循环,所以重点介绍一下OpenMP中for循环的应用.个人感觉只要掌握了文中讲的这些就足够了,如果想要学 ...

  4. OpenMP 并行编程

    OpenMP 并行编程 最近开始学习并行编程,目的是为了提高图像处理的运行速度,用的是VS2012自带的OpenMP. 如何让自己的编译器支持OpenMP: 1) 点击 项目属性页 2)点击 配置 3 ...

  5. OpenMP并行构造的schedule子句详解 (转载)

    原文:http://blog.csdn.net/gengshenghong/article/details/7000979 schedule的语法为: schedule(kind, [chunk_si ...

  6. OpenMP并行程序设计

    1.fork/join并行执行模式的概念 2.OpenMP指令和库函数介绍 3.parallel 指令的用法 4.for指令的使用方法 5 sections和section指令的用法 1.fork/j ...

  7. C++ openmp并行程序在多核linux上如何最大化使用cpu

    以上代码中,#pragma omp parallel for 这一行的作用即是调用openmp的功能,根据检测到的CPU核心数目,将for (i = 0; i < 1000000000; i++ ...

  8. openmp 并行求完数

    // GetWanShu.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include "omp.h" #inclu ...

  9. OpenMP 并行程序设计入门

    OpenMP 是一个编译器指令和库函数的集合,主要是为共享式存储计算机上的并行程序设计使用的. 0. 一段使用 OpenMP 的并行程序 #include <stdio.h> #inclu ...

随机推荐

  1. UML 用例图,时序图,活动图的定义以及区别

    1.用例图,时序图,活动图的定义 1.用例图:  用例图描述了系统提供的一个功能单元.用例图的主要目的是帮助开发团队以一种可视化的方式理解系统的功能需求,包括基于基本流程的"角色" ...

  2. 06.Hibernate实体类生命周期

        前言:Session接口是Hibernate向应用程序提供的操作数据库的主要接口,它提供了基本的增删查改方法,而且Session具有一个缓存它是Hibernate的一级缓存.站在持久化层的角度 ...

  3. 【CentOS】Eclipse中svn插件使用

    目录: 1.安装 2.使用 3.错误 1.安装 svn插件地址: Subclipse 1.6.x Update Site - http://subclipse.tigris.org/update_1. ...

  4. 【Tsinsen】【A1365】森林旅店

    KD-Tree 啊哈~检验了一下自己KD-Tree的学习情况,还算可以,模板至少是记下来了. 支持插入(所以要带重建),查询最近的P个点的距离. 然而题目并没有说是按怎样的顺序输出这P个点?...(事 ...

  5. 【BZOJ】【1028】【JSOI2007】麻将

    暴力/模拟 $n\leq400$,嗯……这是一个很小的数据范围= = 判断一副牌是不是听牌并求出听什么牌太麻烦了,干脆我们直接判是不是胡牌好了~ 枚举胡的是哪张牌,然后判一下加上这张牌后是否能胡. 算 ...

  6. 一道有趣的javascript编程题

    题目:实现以下功能 1. 点击按钮“打开新窗口”,打开新的子页面,要求新窗口的大小为400px X 200px 2. 输入地址信息,点击“确定”按钮,关闭该页面 3. 将子页面中输入的地址信息,回传到 ...

  7. 【面试题】Google of Greater China Test for New Grads of 2014总结

    2014年Google中国校园招聘采用在线技术笔试,在Code Jam平台上,14号9点到15号9点开放测试题,帮助大家熟悉环境.这个周末也有够忙的,当时就上去看了一下,把输入文件下了一下,今天才把题 ...

  8. ab压力测试工具-批量压测脚本

    ab(Apache benchmark)是一款常用的压力测试工具.简单易用,ab的命令行一次只能支持一次测试.如果想要批量执行不同的测试方式,并自动对指标进行分析,那么单靠手工一条一条命令运行ab,估 ...

  9. POJ 1488

    #include <iostream> #include <string> using namespace std; int main() { string s; int i; ...

  10. (转)OpenCV 2.4.8 +VS2010的开发环境配置

    转自:  http://blog.csdn.net/poem_qianmo/article/details/19809337 自己可能需要再进行修改 本系列文章由zhmxy555(毛星云)编写,转载请 ...