并行计算之OpenMP

  在上一篇文章中介绍了并行计算的基础概念,也顺便介绍了OpenMP。

  OpenMp提供了对于并行描述的高层抽象,降低了并行编程的难度和复杂度,这样程序员可以把更多的精力投入到并行算法本身,而非其具体实现细节。对基于数据分集的多线程程序设计,OpenMP是一个很好的选择。同时,使用OpenMP也提供了更强的灵活性,可以较容易的适应不同的并行系统配置。线程粒度和负载平衡等是传统多线程程序设计中的难题,但在OpenMp中,OpenMp库从程序员手中接管了部分这两方面的工作。但是,作为高层抽象,OpenMp并不适合需要复杂的线程间同步和互斥的场合。OpenMp的另一个缺点是不能在非共享内存系统(如计算机集群)上使用,一般在这样的系统上,MPI使用较多。

  在Visual Studio中使用OpenMP其实很简单,只要将 Project 的Properties中C/C++里Language的OpenMP Support开启(参数为 /openmp),就可以让VC++在编译时就可以支持OpenMP 的语法了。而在编写使用OpenMP 的程序时,添加#include <omp.h>即可。下面是一个实例:

#include <stdio.h>
#include <omp.h>
#include <windows.h>
#define MAX_VALUE 10000000

double _test(int value)
{
    int index = 0;
    double result = 0.0;
    for(index = value + 1; index < MAX_VALUE; index +=2 )
        result += 1.0 / index;

    return result;
}

void OpenMPTest()
{
    int index= 0;
    int time1 = 0;
    int time2 = 0;
    double value1 = 0.0, value2 = 0.0;
    double result[2];

    time1 = GetTickCount();
    for(index = 1; index < MAX_VALUE; index ++)
        value1 += 1.0 / index;

    time1 = GetTickCount() - time1;
    memset(result , 0, sizeof(double) * 2);
    time2 = GetTickCount();

#pragma omp parallel for
    for(index = 0; index < 2; index++)
        result[index] = _test(index);

    value2 = result[0] + result[1];
    time2 = GetTickCount() - time2;

    printf("time1 = %d,time2 = %d\n",time1,time2);
    return;
}

int main()
{
    OpenMPTest();

    system("pause");
    return 0;
}

  在这里例子中用到了一个关键的语句:

#pragma omp parallel for

  这个句子代表了C++中使用OpenMP的基本语法规则:pragma omp 指令 [子句[子句]…]

1. OpenMP指令与库函数

  OpenMP包括以下指令

  • parallel:用在一个代码段之前,表示这段代码将被多个线程并行执行
  • for:用于for循环之前,将循环分配到多个线程中并行执行,必须保证每次循环之间无相关性
  • parallel for:parallel 和 for语句的结合,也是用在一个for循环之前,表示for循环的代码将被多个线程并行执行
  • sections:用在可能会被并行执行的代码段之前
  • parallel sections:parallel和sections两个语句的结合
  • critical:用在一段代码临界区之前
  • single:用在一段只被单个线程执行的代码段之前,表示后面的代码段将被单线程执行
  • barrier:用于并行区内代码的线程同步,所有线程执行到barrier时要停止,直到所有线程都执行到barrier时才继续往下执行
  • atomic:用于指定一块内存区域被制动更新
  • master:用于指定一段代码块由主线程执行
  • ordered:用于指定并行区域的循环按顺序执行
  • threadprivate:用于指定一个变量是线程私有的

  OpenMP除上述指令外,还有一些库函数,下面列出几个常用的库函数

  • omp_get_num_procs:返回运行本线程的多处理机的处理器个数
  • omp_get_num_threads:返回当前并行区域中的活动线程个数
  • omp_get_thread_num:返回线程号
  • omp_set_num_threads:设置并行执行代码时的线程个数
  • omp_init_lock:初始化一个简单锁
  • omp_set_lock:上锁操作
  • omp_unset_lock:解锁操作,要和omp_set_lock函数配对使用
  • omp_destroy_lock:omp_init_lock函数的配对操作函数,关闭一个锁

  OpenMP还包括以下子句

  • private:指定每个线程都有它自己的变量私有副本
  • firstprivate:指定每个线程都有它自己的变量私有副本,并且变量要被继承主线程中的初值
  • lastprivate:主要是用来指定将线程中的私有变量的值在并行处理结束后复制回主线程中的对应变量
  • reduce:用来指定一个或多个变量是私有的,并且在并行处理结束后这些变量要执行指定的运算
  • nowait:忽略指定中暗含的等待
  • num_threads:指定线程的个数
  • schedule:指定如何调度for循环迭代
  • shared:指定一个或多个变量为多个线程间的共享变量
  • ordered:用来指定for循环的执行要按顺序执行
  • copyprivate:用于single指令中的指定变量为多个线程的共享变量
  • copyin:用来指定一个threadprivate的变量的值要用主线程的值进行初始化。
  • default:用来指定并行处理区域内的变量的使用方式,缺省是shared

2. parallel指令用法

  parallel 是用来构造一个并行块的,也可以使用其他指令如for、sections等和它配合使用。其用法如下:

#pragma omp parallel [for | sections] [子句[子句]…]
{
  // 需要并行执行的代码
}

  例如,可以写一个简单的并行输出提示信息的代码:

#pragma omp parallel num_threads(8)
{
    printf(“Hello, World!, ThreadId=%d\n”, omp_get_thread_num() );
}

  在本机测试将会得到如下结果:

  结果表明,printf函数被创建了8个线程来执行,并且每一个线程执行的先后次序并不确定。和传统的创建线程函数比起来,OpenMP相当于为一个线程入口函数重复调用创建线程函数来创建线程并等待线程执行完。如果在上面的代码中去掉num_threads(8)来指定线程数目,那么将根据实际CPU核心数目来创建线程数。

3. for指令用法

  for指令则是用来将一个for循环分配到多个线程中执行。for指令一般可以和parallel指令合起来形成parallel for指令使用,也可以单独用在parallel语句的并行块中。其语法如下:

#pragma omp [parallel] for [子句]
    for循环语句

  例如有这样一个例子:

#pragma omp parallel for
for ( int j = 0; j < 4; j++ )
{
    printf("j = %d, ThreadId = %d\n", j, omp_get_thread_num());
}

  可以得到如下结果:

  从结果可以看出,for循环的语句被分配到不同的线程中分开执行了。需要注意的是,如果不添加parallel关键字,那么四次循环将会在同一个线程里执行,结果将会是下面这样的:

  

4. sections和section的用法

  section语句是用在sections语句里用来将sections语句里的代码划分成几个不同的段,每段都并行执行。用法如下:

#pragma omp [parallel] sections [子句]
{
    #pragma omp section
    {
        // 代码块
    }
}

  例如有这样一个例子:

#pragma omp parallel sections
{
#pragma omp section
    printf("section 1 ThreadId = %d\n", omp_get_thread_num());
#pragma omp section
    printf("section 2 ThreadId = %d\n", omp_get_thread_num());
#pragma omp section
    printf("section 3 ThreadId = %d\n", omp_get_thread_num());
#pragma omp section
    printf("section 4 ThreadId = %d\n", omp_get_thread_num());
}

  可以得到如下结果:

  结果表明,每一个section内部的代码都是(分配到不同的线程中)并行执行的。使用section语句时,需要注意的是这种方式需要保证各个section里的代码执行时间相差不大,否则某个section执行时间比其他section长太多就达不到并行执行的效果了。

  如果将上面的代码拆分成两个sections,即:

#pragma omp parallel sections
{
#pragma omp section
    printf("section 1 ThreadId = %d\n", omp_get_thread_num());
#pragma omp section
    printf("section 2 ThreadId = %d\n", omp_get_thread_num());
}

#pragma omp parallel sections
{
#pragma omp section
    printf("section 3 ThreadId = %d\n", omp_get_thread_num());
#pragma omp section
    printf("section 4 ThreadId = %d\n", omp_get_thread_num());
}

  产生的结果将会是这样的:

  可以看出,两个sections之间是串行执行的,而section内部则是并行执行的。

小节:

  用for语句来分摊任务是由系统自动进行的,只要每次循环间没有时间上的差距,那么分摊是很均匀的,使用section来划分线程是一种手工划分线程的方式,最终并行性的好坏依赖于程序员。

  本篇文章中讲的几个OpenMP指令parallel, for, sections, section实际上都是用来如何创建线程的,这种创建线程的方式比起传统调用创建线程函数创建线程要更方便,并且更高效。

  

时间: 2024-10-11 10:39:07

并行计算之OpenMP的相关文章

并行计算之OpenMP中的任务调度

本文参考<OpenMP中的任务调度>博文,主要讲的是OpenMP中的schedule子句用法. 一.应用需求 在OpenMP并行计算中,任务调度主要用于并行的for循环.当for循环中每次迭代的计算量相差较大时,如果简单的为每次迭代分配相同的线程,就会导致线程任务不均衡,CPU资源没有被充分利用,影响程序执行性能.例如下面这种情况: int i, j; int a[100][100] = {0}; for ( i = 0; i < 100; ++i ) { for( j = i; j &

深入理解FFM原理与实践

原文:http://tech.meituan.com/deep-understanding-of-ffm-principles-and-practices.html 深入理解FFM原理与实践 del2z, 大龙 ·2016-03-03 09:00 FM和FFM模型是最近几年提出的模型,凭借其在数据量比较大并且特征稀疏的情况下,仍然能够得到优秀的性能和效果的特性,屡次在各大公司举办的CTR预估比赛中获得不错的战绩.美团点评技术团队在搭建DSP的过程中,探索并使用了FM和FFM模型进行CTR和CVR

并行计算大作业之多边形相交(OpenMP、MPI、Java、Windows)

***************************************转载请注明出处:http://blog.csdn.net/lttree******************************************** 吐槽: 话说,相当郁闷,2015年,第一次打开博客,准备总结一下这一年.. 结果博客被封了= =! 今天,终于解封了,换了密码,换了密保.... 但是,写回顾的激情有点退散了.. 明后两天要上课,明天还要验收一个综合设计大作业,再后两天要考试,再后两天继续上课,

如何利用VS的代码优化和openmp并行计算提高程序运行速度

以前利用多线程为拥有较大量计算的程序提速过,但也深知多线程的同步和程序的调试是一大坑,最近为实验室的项目学习了一点VS下优化代码的设置以及利用openmp加速运算,都是些很基本的提升程序速度的方法,只要稍微修改下代码和设置,就可使程序加速.配合多线程,经过clock()函数验证,我的程序运行可提高60%的速度,详细如下: 代码优化: 属性->配置属性->C/C++->代码生成:启用增强指令集,可选用 流式处理 SIMD 扩展 2 (/arch:SSE2) (/arch:SSE2).流式处

Windows和Linux系统下并行计算环境MPI和OpenMP的搭建

目录 windows平台下在Visual Studio2019配置MPI环境 MPI下载安装 项目配置 测试 Linux下配置MPI编程环境-Ubuntu 18.04 终端配置过程 Windows系统下OpenMP配置 visual studio2019配置 测试 windows平台下在Visual Studio2019配置MPI环境 MPI下载安装 MPI windows版本的下载地址:https://docs.microsoft.com/en-us/message-passing-inter

OpenMp实现并行化

前言 昨天,往arm上移植opencv程序,发现运行速度很慢.观察资源监视器发现只有一个核处于高负荷(总共4核),遂考虑到需要多核计算.OpenMp和MPI是常用并行计算库,OpenMP相对简单适合单机多核多线程,MPI适合集群,但复杂. OpenMp是由OpenMP Architecture Review Board牵头提出的,并已被广泛接受的,用于共享内存并行系统的多处理器程序设计的一套指导性的编译处理方案(Compiler Directive).OpenMP支持的编程语言包括C语言.C++

应用OpenMP的一个简单的设计模式

小喵的唠叨话:最近很久没写博客了,一是因为之前写的LSoftmax后馈一直没有成功,所以在等作者的源码.二是最近没什么想写的东西.前两天,在预处理图片的时候,发现处理200w张图片,跑了一晚上也才处理完一半.早上的时候,出于无奈,花半小时改写了一个简单调用OpenMP的处理程序,用了30个核心,然后一小时不到就处理完了.感慨在多核的时代,即使是简单的程序,如果能支持多核,应该都能节省不少时间. 本文系原创,转载请注明出处~ 小喵的博客:http://www.miaoerduo.com 博客原文:

baike并行计算概念

并行计算 概论 ? 高性能计算 ? 计算机集群 ? 分布式计算 ? 网格计算 ? 云端运算         方式 ? Bit-level parallelism ? Instruction level parallelism ? Data parallelism ? 任务并行   理论 ? Speedup ? Amdahl定理 ? Flynn's taxonomy ? Cost efficiency ? Gustafson定理 ? Karp-Flatt metric       元素 ? 进程

高性能并行计算计系统概述

高性能并行计算系统 1.微处理器的存储结构 2.Cache结构对程序性能的影响 例如 矩阵乘法中不同循环顺序对程序性能的影响. DO J=1,N DO I=1,N C(I,J) = 0.D0 ENDDOENDDODO I=1,N DO J=1,N DO K=1,N C(I,J) = C(I,J) + A(I,K) * B(K,J) ENDDO ENDDOENDDO 3.共享内存SMP型并行计算机 对称多处理器(Symmetric Multi-Processors),或共享内存处理器(Shared