linux 并行计算之 openmp初探

一:简介

      OpenMP(Open Multi-Processing)是一种共享内存编程模式,多线程并行应用程序界面,使用C,C++和Fortran语言。由两种形式实现并行功能:编译指导语句运行时库函数。编译指导语句告诉程序何时开始并行,库函数用来设置线程数及实现其它并行功能。

                                                 linux 并行计算之 openmp初探

                         linux 并行计算之 openmp初探

基本概念:

1. 串行执行区:只能有单个线程执行的代码

2. 并行执行区:可有多个线程执行的代码
     循环
     独立的代码块

3. 变量作用域:
    共享变量
    私有变量


4. 数据相关
    流相关(flow dependence) r1=r2+r0; r3=r1+2;
    输出相关(output dependence) r1=r0+a; r1=3+4*r0
    反相关(anti-dependence)a=b+c; b=3*r;

二:两个小程序

     1. Hello_World.c 的并行

#include <stdio.h>
#include <omp.h>//调用Openmp库函数
#define N 6

int main(int argc, char *argv[])
{
  int i;
  printf ("*Hello World! Thread: %d\n",
         omp_get_thread_num());

  #pragma omp parallel for
    for (i = 0; i < N; ++i)
      printf ("Hello World!  Thread: %d, i: %d\n",
              omp_get_thread_num(), i);
   return 0;
}

其中:#pragma omp parallel for 是编译指导语句,声明下面的for语句要并行多线程执行。

           omp_get_thread_num()是运行时的库函数,用来获取当前的线程号。

通过命令行

gcc -fopenmp Hello_World_omp.c -o hw_opmp

编译,生成可执行文件。运行程序:

./hw_opmp

结果如下:

$ ./hw_opmp
*Hello World! Thread: 0
Hello World!  Thread: 2, i: 4
Hello World!  Thread: 1, i: 2
Hello World!  Thread: 1, i: 3
Hello World!  Thread: 0, i: 0
Hello World!  Thread: 0, i: 1
Hello World!  Thread: 3, i: 5

可以看到,没有并行时,默认只有一个线程,ID 为0;当并行时,有4个线程,即通过4个CPU 来同时处理,我的电脑只有4个CPU 核心。

2. matrix_multiply_openmp.cpp

#include<iostream>
#include<omp.h>
#include<time.h>//记录程序运行时间
using namespace std;

int  main()
{ 
   int Matrix1[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};
   int Matrix2[4][2]={2,3,5,4,7,9,8,0};
   int Matrix[3][2];
   clock_t start, end;//声明类型为clock_t

   cout<<"Matrix1:\n";
   int i,j,k;
   for(i=0;i<3;i++){
      for(j=0;j<4;j++){
          cout<<Matrix1[i][j]<<'\t';
    }
    cout<<endl;
   }

  cout<<"Matrix2:\n";
  for(i=0;i<4;i++){
      for(j=0;j<2;j++){
          cout<<Matrix2[i][j]<<'\t';
    }
   cout<<endl;
  }

  for(i=0;i<3;i++){
     for(j=0;j<2;j++){
             Matrix[i][j]=0;
      }
  }
   
  omp_set_num_threads(3);
  int pnum=omp_get_num_procs();
  cout<<"Thread_pnum ="<<pnum<<endl;
  
 start=clock();//开始计时
#pragma omp parallel shared(Matrix1, Matrix2, Matrix) private(j,k)
{
#pragma omp for schedule(dynamic)
 for(i=0;i<3;i++){
     cout<<"Thread_num:"<<omp_get_thread_num()<<'\n';
     for(j=0;j<2;j++){
         for(k=0;k<4;k++){
                 Matrix[i][j]=Matrix[i][j]+Matrix1[i][k]*Matrix2[k][j];
         }
      }
  }
}
 end=clock();
 cout<<"Matrix multiply time:"<<(double (end-start))/CLOCKS_PER_SEC<<endl; //CLOCKS_PER_SEC为每秒敲钟数,end-start是从开始到程序结束的总敲钟数
 cout<<"The result is:\n";

 for(i=0;i<3;i++){
    for(j=0;j<2;j++){
     cout<<Matrix[i][j]<<'\t';
    }
   cout<<endl;
  }
 
 return 0;
}

其中:omp_set_num_threads(3);  设置进程数为3
          omp_get_num_procs();  获取本机处理器核数,我的是4核。

         #pragma omp parallel shared(Matrix1, Matrix2, Matrix) private(j,k)   编译指导语句,并设置Matrix1,Matrix2, Matrix为共享,j,k为私有。这是为了让3个进程都能使用Matrix1,Matrix2, Matrix,但每个进程计算自己的向量运算。
         #pragma omp for //schedule(dynamic)  编译指导语句,声明下面的for循环,即第一个for循环进入3线程的并行计算。由于Matrix1是3行,故每个线程处理一行。schedule(dynamic)动态的为空闲进程分配指定大小的循环块。

注意:并行化for的注意事项:

(1)循环语句规范,易于判断循环次数;

(2)循环中不能包含允许循环提前退出的语句(将改变循环次数),如:break, return, exit.

命令行编译:

g++ -fopenmp matrix_multiply_openmp.cpp -o mm_openmp

c++源文件的编译使用g++,  c源文件的编译使用gcc。

运行

./mm_openmp

$ ./mm_openmp
Matrix1:
1	2	3	4	
5	6	7	8	
9	10	11	12	
Matrix2:
2	3	
5	4	
7	9	
8	0	
Thread_pnum =4
Thread_num:Thread_num:Thread_num:0
2
3
Matrix multiply time:0.007634
The result is:
65	38	
153	102	
241	166	

可以看到程序使用了ID 为0,2,3的进程来处理第一个for循环。