小白说OpenMP:从头到尾

来源:互联网 发布:六级仔细阅读技巧知乎 编辑:程序博客网 时间:2024/05/17 15:04

初衷

带并行课的上机,自己并不会OpenOMP,因此打算抽时间学习并记录一下。 我准备利用一个博客的内容,将OpenOMP的基本知识点过一遍,要求有基本介绍,个人的分析,示例代码以及运行结果截图; 时间关系,自己并没有太多的精力挖掘高级知识点的用途,因此就不作阐述了。编译环境为visual studio2013.

简介

当前cpu基本都是走向多核架构,通过多线程编程可以充分发挥多核cpu的优势,从而缩短程序的运行时间。 可是,大多数人都是谈“多线程”色变,这是因为多线程编程总会涌现出各种稀奇古怪的bug,而其调试又是极其痛苦的,这些都无形中提高了程序员进入多线程的门槛,高效却不易用。 因此,有组织开始考虑能否只通过使用几条简单的编译器指令(而不需要写复杂的线程代码)获得多线程带来的好处,openmp应运而生。

OpenMP是由OpenMP Architecture Review Board牵头提出的,并已被广泛接受的,用于共享内存并行系统的多处理器程序设计的一套指导性的编译处理方案(Compiler Directive),当前支持Fortran和C++。 在支持OpenMP的编译上,你只需要在你的代码中加入几条额外的编译指令,就可以立刻获得多线程代码的加速效果。 程序员从线程创建,数据共享等等的复杂的工作中解放出来,从而专注于如何组织并行。

如何在vs中使用openmp呢? 特别简单,只需要在工程,右键-》属性-》c/c++-》语言-》openmp支持,开启openmp即可,具体如下图:
这里写图片描述

下面先通过一个特别简单例子,来说明利用OpenMp进行多线程编程。

#include <iostream>#include <vector>#include <omp.h>#include <windows.h>#include <stdlib.h>#include <thread>using namespace std;vector<double> vec;int VEC_SIZE = 5000000;void serial_add(){    for (int i = 0; i < VEC_SIZE; ++i)    {        vec[i] = sqrt(vec[i]) + i;        vec[i] = sqrt(vec[i]) + i;        vec[i] = sqrt(vec[i]) + i;    }}void mp_add(){#pragma omp parallel    {#pragma omp for        for (int i = 0; i < VEC_SIZE; ++i)        {            vec[i] = sqrt(vec[i]) + i;            vec[i] = sqrt(vec[i]) + i;            vec[i] = sqrt(vec[i]) + i;        }    }}//multithreadvoid thread_func(int start, int end){    for (int i = start; i < end; ++i)    {        vec[i] = sqrt(vec[i]) + i;        vec[i] = sqrt(vec[i]) + i;        vec[i] = sqrt(vec[i]) + i;    }}void multhread_add(){    thread t1(thread_func, 0, VEC_SIZE/4);    thread t2(thread_func, VEC_SIZE / 4, VEC_SIZE/2);    thread t3(thread_func, VEC_SIZE / 2, VEC_SIZE/4*3);    thread t4(thread_func, VEC_SIZE / 4*3, VEC_SIZE);    t1.join();    t2.join();    t3.join();    t4.join();}typedef void (*pfunc) ();void getTime(pfunc func){    double start, end;    start = GetTickCount();    func();    end = GetTickCount();    cout << func << " cost " << end - start << " ms" << endl;}int main(){    srand(0);    for (int i = 0; i < VEC_SIZE; ++i)    {        vec.push_back(1.0*rand() / 10000);    }    getTime(serial_add);    getTime(mp_add);    getTime(multhread_add);    getchar();    return 0;}

代码解释

首先main函数创建一个非常大的数组vec,然后定义一个对所有元素开根号的计算过程。一共使用3种策略对数组进行计算,第一种serial_add,基本的串行运算过程,第二种mp_add,使用openmp编译指令,第三种multhread_add,手动创建多线程执行。

三种方式执行完的时间开销如下:
按顺序依次是serial, openmp, multhread
这里写图片描述
结论1:
多线程确实快。 可以看出openmp和multhread两种方式的时间基本相同,都大约是串行版本的1/2。我本机的电脑是2核的,可以满足最多两个线程同时执行,考虑到需要把cpu尽可能的利用上,我在multhread的版本中手动方式创建4个线程比较。
结论2:
openmp编程确实简单。 可以看出为了获得多线程的优势,openmp方式只需要两条编译器执行就可以了,而手动创建方式就麻烦的多,即使利用了新的c++ thread库,仍然需要10多行的代码。如果利用再需要互斥,同步,数据共享之类的操作就更麻烦了。
结论3:
openmp的扩展性不如手动创建的方式。 这个很容易理解,简单的编译指令能支持的类型毕竟有限,而手动创建可以有各种各样的执行操作。不过openmp作为上层编译指令,经过这么久的发展,基本的操作基本上都具备,完全能够满足日常的使用。

未完待续。

0 0