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
相关推荐
耶啵奶膘44 分钟前
uniapp-是否删除
linux·前端·uni-app
奋斗的小花生1 小时前
c++ 多态性
开发语言·c++
Nu11PointerException1 小时前
JAVA笔记 | ResponseBodyEmitter等异步流式接口快速学习
笔记·学习
闲晨1 小时前
C++ 继承:代码传承的魔法棒,开启奇幻编程之旅
java·c语言·开发语言·c++·经验分享
2401_850410832 小时前
文件系统和日志管理
linux·运维·服务器
XMYX-02 小时前
使用 SSH 蜜罐提升安全性和记录攻击活动
linux·ssh
UestcXiye3 小时前
《TCP/IP网络编程》学习笔记 | Chapter 3:地址族与数据序列
c++·计算机网络·ip·tcp
亦枫Leonlew3 小时前
三维测量与建模笔记 - 3.3 张正友标定法
笔记·相机标定·三维重建·张正友标定法
考试宝3 小时前
国家宠物美容师职业技能等级评价(高级)理论考试题
经验分享·笔记·职场和发展·学习方法·业界资讯·宠物
霁月风4 小时前
设计模式——适配器模式
c++·适配器模式