使用openmp進行並行編程

預處理指令pragma

在系統中加入預處理器指令通常是用來容許不是基本c語言規範部分的行爲。不支持pragma的編譯器會忽略pragma指令提示的那些語句,這樣就容許使用pragma的程序在不支持它們的平臺上運行。c++

第一個程序:hello

#include <stdio.h>
#include <stdlib.h>
#include <omp.h>

void Hello(void); // Thread function

int main(int argc, char* argv[]) {  
	// Get number of threads from command line  
	int thread_count = strtol(argv[1], NULL, 10);
	#pragma omp parallel num_threads(thread_count)  
	Hello();
    return 0;
}

void Hello(void) {
	int my_rank = omp_get_thread_num();
	int thread_count = omp_get_num_threads();

	printf("Hello from thread %d of %dnn\n", my_rank, thread_count);
}

Hello例子的分析:

最基本的並行原語

用於運行代碼塊的線程數能夠動態生成。程序員

pragma omp parallel  :

當程序到達parallel指令時,原來的線程繼續執行,另外的線程被啓動。在openmp語法中,執行並行塊的線程集合(原始線程和新的線程被稱爲線程組,原始的線程被稱爲主線程,額外的線程稱爲從線程。每一個線程組成員都調用指令後的代碼塊。算法

num_thread( )

# pragma omp parallel num_threads ( thread_count )

一個從句例子(用於修飾原語),可用於指定線程數量app

omp.h

#include <omp.h>

使用openmp必須含omp.h頭文件ide

strtol( )

long strtol(const char* number p,char** end p,int base);

使用stdlib.h中的strtol來得到線程數
ps:一些系統因素可能會限制能夠啓動的線程數量;OpenMP 並不保證可以啓動指定個的線程;函數

多數系統可以啓動上百甚至上千的線程;除非啓動的太多,通常都能知足要求。ui

例子:梯形積分法

圖片

若是每一個子區間有相同的寬度,而且定義h=(b-a)/n,xi=a+ih,i=0, 1, ..., n,那麼近似值將是:線程

圖片

//串行算法實現//
 Input: a, b, n ;
 h = (b*a)/n;
 approx = (f(a) + f(b))/2.0;
 for (i = 1; i <= n-1; i++) {
 	x_i = a + i*h;
 	approx += f(x_i);
 }
 approx = h*approx;

第一種嘗試

  1. 定義兩種類型的任務:

​ a) 計算單個梯形的面積;設計

​ b) 將面積加起來。指針

  1. 在第一階段,沒有通訊開銷;但第二階段每一個任務須要通訊。

考慮一個問題:結果不可預估——引入互斥量

pragma omp critical   global_result += my_result ;

第一個版本

 #include <stdio.h>
 #include <stdlib.h>
 #include <omp.h>

  void Trap(double a, double b, int n, double  global_result p);
  int main(int argc, char  argv[]){
    double  global_result = 0.0;
    double  a, b;
    int     n;
    int     thread_count;

    thread_count = strtol(argv[1], NULL, 10);
    printf("Enter a, b, and n n");
    scanf("%lf %lf %d", &a, &b, &n);
#   pragma omp parallel num_threads(thread_count)
    Trap(a, b, n, &global_result);

    printf("With n = %d trapezoids, our estimate n", n);
    printf("of the integral from %f to %f = %.14e n",
    a, b, global_result);
    return 0;
}    /∗  main ∗/
    
    void Trap(double a, double b, int n, double* global_result_p)
      double  h, x, my_result;
      double  local_a, local_b;
      int  i, local n;
      int my_rank = omp_get_thread_num();
      int thread_count = omp_get_num_threads();
        
      h = (b−a)/n;
      local_n = n/thread_count;
      local_a = a + my_rank*local_n*h;
      local_b = local_a + local_n*h;
      my_result = (f(local_a) + f(local_b))/2.0;
      for (i = 1; i <= local_n−1; i++){
        x = local_a + i*h;
        my_result += f(x);
       }
  `   ` my_result = my_result*h;
   #  pragma omp critical
      ∗global_result_p += my_result;
  }    /∗  Trap ∗/

做用域

在串行程序中, 變量的做用域包含了全部可使用變量的區域;

在OpenMP中, 變量的做用域還要包括能夠訪問該變量的並行區域。

能被全部線程訪問的變量具備 shared(共享) 做用域;

只能被一個線程訪問的變量具備 private (私有)做用域.

默認的做用域是 shared.

規約從句:

替代(在parallel塊中聲明一個私有變量和將臨界區移到函數調用之)

歸約:將相同的歸約操做符重複的應用到操做數序列來獲得一個結果的計算。

全部操做的中間結果存儲在一個變量中:歸約變量

reduction(<operator>:<variable list>)

新的代碼:

global_result = 0.0;
#  pragma omp parallel num threads(thread count)\
 reduction(+: global_result)
global_result += Local_trap(double a, double b, int n);

parallel for

可以生成一隊線程來執行接下來的語句塊;

語句塊必須是一個for循環;

經過將循環切分給不一樣的線程來實現並行。

只有迭代次數肯定的循環才能夠被並行化。

h = (b−a)/n;
approx = (f(a) + f(b))/2.0;
#  pragma omp parallel for num threads(thread_count) reduction(+: approx)
for (i = 1; i <= n−1; i++)
approx += f(a + i∗h); approx = h∗approx;

可被並行化的for循環形式:
圖片

**ps: **index 必須是整數或者指針 (e.g., 不能是浮點數);

start, end, 和 incr 必須具備相應的類型。 例如, 若是index 是一個指針, 那麼 incr 必須是一個整型;

start, end, 和 incr 在循環執行過程當中不能被修改;

在循環執行過程當中, 變量 index 只能被for語句修改。

數據依賴

1.OpenMP 編譯器並不檢查循環迭代中的數據依賴問題;

2.通常來講,OpenMP沒法處理帶有數據依賴的循環。

解決思路:設計私有變量而且保證其私有做用域(private子句)

default子句

編譯器強制要求程序員指定在塊中使用的外部變量的做用範圍。

double sum = 0.0;
# pragma omp parallel for num threads(thread count)\
default(none) reduction(+:sum) private(k, factor)\
 shared(n)
for (k = 0; k < n; k++){
  if (k % 2 == 0)
    factor = 1.0;
  else
    factor = −1.0;
  sum += factor/(2∗k+1);
}

for指令

並不建立線程,使用已經在parallel塊中建立的線程。

#  pragma omp for

解決循環調用問題:schedule ( type , chunksize )

type 能夠是:
static: 提早把任務分配好;

dynamic or guided: 在運行時動態分配;

dynamic:

任務被分紅 chunksize 大小的連續段;

每一個線程執行一小塊, 當有一個線程執行完時, 它會請求得到1個新的;

重複上述過程,直到完成計算;

chunksize 能夠被去掉;當去掉時, chunksize 默認爲1.

guided:

每一個線程執行一小塊, 當有一個線程執行完時, 它會請求得到1個新的;

可是,新的任務塊是不斷變小的;

若是不指定chunksize,那麼默認會降到1.

若是指定了chunksize, 則會降到指定的chunksize, 除了最後一塊可能小於chunksize.

auto: 編譯器或者運行時系統決定調度策略;

runtime: 運行時決定。

chunksize 是一個正整數

相關文章
相關標籤/搜索