openMP学习笔记 -编程模型

OpenMP模型

gcc编译openmp指令:gcc test.cpp -o test -fopenmp

定积分计算 函数面积

给定一个定积分,计算其面积:
∫ 0 1 4.0 ( 1 + x 2 ) d x \int^{1}_{0}{\frac{4.0}{(1+x^2)}dx} ∫01(1+x2)4.0dx

omp 概念

并行区域

并行区域用于多线程并行执行指令

c++ 复制代码
#pragma omp parallel
{
    ... do lots of stuff
}//end of parallel region

临界区

临界区用于解决:内存竞争问题,该区块中的代码只能有一个线程执行,其他线程必须等待。

c++ 复制代码
#pragma omp critical
{
    ... one or more lines of code
}

栅栏

栅栏用于解决线程同步问题,通过栅栏设置的锚点,必须等所有线程均执行到该位置才能继续往下执行。

c++ 复制代码
#pragma omp barrier
//... continue work

栅栏通常用于这种情况,即必须在所有线程完成栅栏前的工作内容,才能继续往下执行。栅栏后的工作内容应当依赖所有线程栅栏前的工作全部完成才能执行,否则得不偿失。

因为栅栏所带来开销很大。这一点相当于cuda中的同步函数。

omp相关函数

  • int omp_get_num_threads() : 获取正在运行的线程数
  • int omp_get_thread_num() : 获取线程id
  • void omp_set_num_threads(int) : 获取要执行的线程数
  • double omp_get_wtime() : 返回以秒为单位的时间

串行执行

以横坐标为单位划分为指定数量的小块,取每个小块的中值作为该块的值,累加获得最终值。代码如下:

c++ 复制代码
#include <stdio.h>
#include <omp.h>
#include <iostream>
static long num_steps = 100000000;
double step;
int main(){
  int i;
  double x , pi , sum=0.0;
  double start_time , run_time;
  step = 1.0 / (double)num_steps;
  start_time = omp_get_wtime();
  for (int i=0; i<num_steps; ++i) {
    x = (i+0.5)*step;
    sum += 4.0 / (1.0 +x*x);
  }
  pi = step*sum;
  run_time = omp_get_wtime() - start_time;
  std::cout<<"area result: "<<pi<<" steps: "<<num_steps<<" runtime: "<<run_time<<std::endl;
  return 0;
}

以padding的方式解决多线程计算总和问题

c++ 复制代码
#include <stdio.h>
#include <omp.h>
#include <iostream>
#include <math.h>
#include <functional>
#define NTHREADS 12
double func(const double &x){
  return 4.0/(1.0+x*x);
}
template<typename FunctionType> double Area(FunctionType func,const double &start, 
                                            const double &end, const long &num_steps){
  double width = (end-start)/num_steps;
  double hight[NTHREADS]={0.0};
  int actual_RunThreads;
  // omp_set_num_threads(NTHREADS);
  double start_time = omp_get_wtime();
  #pragma omp parallel
  {
    //获取线程id
    long thread_ID = omp_get_thread_num();
    //获取实际运行的线程数
    if(!thread_ID) actual_RunThreads = omp_get_num_threads();
    for(long i = thread_ID; i<num_steps ; i+=actual_RunThreads){
      double x = start +(i+0.5)*width;
      hight[thread_ID]+=func(x);
    }
  }
  double hightSum=0.0;
  for (int i =0;i!=actual_RunThreads ; ++i) {
    hightSum+=hight[i]; 
  }
  double runtime = omp_get_wtime()-start_time;
  std::cout<<"runtime: "<<runtime<<" actual_RunThreads: "<<actual_RunThreads<<std::endl;
  return hightSum*width;
}

template<typename FunctionType, typename... Args> double AreaAgent(FunctionType func ,Args... args){
  return Area( func, args...);
}

int main(){
  std::function<double(const double&)> f = func;
  double area =  AreaAgent(f , 0.0 , 1.0 , 100000000);
  std::cout<<"area: "<<area<<std::endl;
  return 0;
}

以临界区方式计算面积总和问题

c++ 复制代码
#include <stdio.h>
#include <omp.h>
#include <iostream>
#include <math.h>
#include <functional>
double func(const double &x){
  return 4.0/(1.0+x*x);
}
template<typename FunctionType> double Area(FunctionType func,const double &start, 
                                            const double &end, const long &num_steps){
  //获取每块的宽度
  //num_steps表示分为多少块
  double width = (end-start)/num_steps;
  double sum=0.0;
  int actual_RunThreads;
  double start_time = omp_get_wtime();
  //并行执行区域
  #pragma omp parallel
  {
    double partial_sum=0.0;
    //获取线程id
    long thread_ID = omp_get_thread_num();
    //获取实际运行的线程数
    if(!thread_ID) actual_RunThreads = omp_get_num_threads();
    //actual_RunThreads表示步长
    for(long i = thread_ID; i<num_steps ; i+=actual_RunThreads){
      //以块中间的函数值作为整块的面积
      double x = start +(i+0.5)*width;
      partial_sum+=func(x);
    }
    //互斥方式计算总面积,相当于原子操作
    #pragma omp critical
    {
      sum+=partial_sum;
    }
  }

  double runtime = omp_get_wtime()-start_time;
  std::cout<<"runtime: "<<runtime<<" actual_RunThreads: "<<actual_RunThreads<<std::endl;
  return sum*width;
}

template<typename FunctionType, typename... Args> double AreaAgent(FunctionType func ,Args... args){
  return Area( func, args...);
}

int main(){
  std::function<double(const double&)> f = func;
  double area =  AreaAgent(f , 0.0 , 1.0 , 100000000);
  std::cout<<"area: "<<area<<std::endl;
  return 0;
}

执行时间统计

线程数 串行 OMP OMP消除伪共享 OMP临界区同步
1 0.290821 2.49737 2.51376 2.50391
2 0.290821 2.46001 2.55435 1.25341
3 0.290821 2.92166 1.90781 0.991777
4 0.290821 4.09134 1.26799 0.630626
5 0.290821 3.13144 2.53815 0.627318
6 0.290821 2.93112 1.24541 0.579776
7 0.290821 2.83655 2.95334 0.527468
8 0.290821 2.9195 0.826844 0.458585
9 0.290821 2.39431 0.852851 0.409409
10 0.290821 3.20907 0.82156 0.381328
11 0.290821 3.02943 1.91977 0.352992
12 0.290821 2.95709 0.737233 0.343221
相关推荐
leoufung2 分钟前
VIM FZF 安裝和使用
linux·编辑器·vim
世事如云有卷舒11 分钟前
FreeRTOS学习笔记
笔记·学习
一个小白118 分钟前
C++——list模拟实现
开发语言·c++
程序员老舅32 分钟前
C++ Qt项目教程:WebServer网络测试工具
c++·qt·测试工具·webserver·qt项目·qt项目实战
靡不有初1111 小时前
CCF-CSP第18次认证第一题——报数【两个与string相关的函数的使用】
c++·学习·ccfcsp
bugtraq20211 小时前
XiaoMi Mi5(gemini) 刷入Ubuntu Touch 16.04——安卓手机刷入Linux
linux·运维·ubuntu
CodeWithMe2 小时前
[ Vim ] 常用命令 and 配置
linux·编辑器·vim
DC_BLOG2 小时前
Linux-GlusterFS进阶分布式卷
linux·运维·服务器·分布式
gu202 小时前
c#编程:学习Linq,重几个简单示例开始
开发语言·学习·c#·linq
cookies_s_s2 小时前
Linux--进程(进程虚拟地址空间、页表、进程控制、实现简易shell)
linux·运维·服务器·数据结构·c++·算法·哈希算法