高性能計算複習

第一二章

高性能計算概念

高性能計算(High performance computing,縮寫HPC) 指通
常使用很多處理器(作爲單個機器的一部分)或者某一集羣
中組織的幾臺計算機(作爲單個計算資源操作)的計算系統
和環境

性能衡量單位

floats

K M G T P
3 6 9 12 15 18
百萬 十億 萬億 千萬億 百億億

並行硬件

  • Flynn 經典分類:SISD, SIMD, MISD, MIMD
  • 內存結構分類:分佈式內存系統,共享內存系統

並行軟件

  • 分佈式內存編程 MPI MapReduce
  • 共享內存編程 Pthreads openmp
  • GPU編程  CUDA OpenCL

第三章(MPI)

點對點通信函數

  • 概念

  • MPI_Send()
    MPI_Send(sendbuf,count, datatype, dest_proc,tag,comm);

  • MPI_Recv()
    MPI_Recv(recvbuf, count, datatype,src_proc.tag,comm,&status);

  • 問候程序

//Hello World的並行(mpi)
#include <stdio.h>
#include<string.h>
#include<mpi.h>

int main(int argc,char *argv[])
{
    int rc,i;
    int comm_sz,my_rank;
    char message[100];
    MPI_Status status;
    int MAX_STRING = 100;
    rc=MPI_Init(NULL,NULL);
    MPI_Comm_size(MPI_COMM_WORLD,&comm_sz);
    
    MPI_Comm_rank(MPI_COMM_WORLD,&my_rank);
    //hello ,rc=0,MPI_SUCCESS=0(輸出四句)
    //printf("hello ,rc=%d,MPI_SUCCESS=%d\n",rc,MPI_SUCCESS);
    printf("hello ,I am %d of %d\n",my_rank,comm_sz);
    if(my_rank!=0)
    {
        strcpy(message,"hello");
        //MPI_Send(message,strlen(message)+1,MPI_CHAR,0,99,MPI_COMM_WORLD);
        MPI_Send(message,MAX_STRING,MPI_CHAR,0,99,MPI_COMM_WORLD);
    }
    else
    {
        for(i=1;i<comm_sz;i++)
        {
          // MPI_Recv(message,100,MPI_CHAR,MPI_ANY_SOURCE,99,MPI_COMM_WORLD,MPI_STATUS_IGNORE);
          
            MPI_Recv(message,100,MPI_CHAR,MPI_ANY_SOURCE,MPI_ANY_TAG,MPI_COMM_WORLD,&status);
            printf("%s from %d\n",message,i);
        }
    }

    MPI_Finalize();
 
    return 0;
}

在這裏插入圖片描述

集合通信函數

概念

  • MPI_Reduce()
    MPI_Reduce(send_buf,recv_buf,count,datatype,op,dest_proc,comm);
    梯形積分

  • MPI_Scatter()
    MPI_Scatter(send_buf_p,send_count,send_type,recv_buf_p,recv_count,recv_type,src_proc,comm)

  • MPI_Bcast()
    MPI_Bcast(data_p,count,datatype,src_proc,comm);

  • MPI_Gather()
    MPI_Gather(send_buf_p,send_count,send_type,recv_buf_p,recv_count,recv_type,dest_proc,comm)

向量相加
矩陣向量相乘

梯形積分

  • 梯形積分法,使其能夠在comm_sz無法被n整除的情況下,正確估計積分值(假設n>=comm_sz)
/*
 * mpicc -lm -g -Wall -o ex2_tk2_1 ex2_tk2_1.c && mpiexec -n 5 ./ex2_tk2_1
 */

#include <stdio.h>
#include<math.h>
#include<mpi.h>
double f(double x)
{
    return sin(x);
}
double Trap(double a,double b,double n,double h)
{
    double estimate,x;
    int i;
    estimate=(f(a)+f(b))/2.0;
    for(i=1;i<n;i++)
    {
       x=a+i*h;
       estimate+=f(x);
       
    }
    return estimate*h;
}
void Get_input(int my_rank,int comm_sz,double* a_p,double* b_p,int* n_p)
{
    int dest;
    if(my_rank==0)
    {
        printf("Enter a,b,and n\n");
        scanf("%lf %lf %d",a_p,b_p,n_p);
        for(dest=1;dest<comm_sz;dest++)
        {
           MPI_Send(a_p,1,MPI_DOUBLE,dest,0,MPI_COMM_WORLD);
           MPI_Send(b_p,1,MPI_DOUBLE,dest,0,MPI_COMM_WORLD);
           MPI_Send(n_p,1,MPI_INT,dest,0,MPI_COMM_WORLD);

        }
    }
    else
    {
        MPI_Recv(a_p,1,MPI_DOUBLE,MPI_ANY_SOURCE,MPI_ANY_TAG,MPI_COMM_WORLD,MPI_STATUS_IGNORE);
        MPI_Recv(b_p,1,MPI_DOUBLE,MPI_ANY_SOURCE,MPI_ANY_TAG,MPI_COMM_WORLD,MPI_STATUS_IGNORE);
        MPI_Recv(n_p,1,MPI_INT,MPI_ANY_SOURCE,MPI_ANY_TAG,MPI_COMM_WORLD,MPI_STATUS_IGNORE);

    }

}

int main()
{
    double a=0.0,b=3.0;
    int n=2048,my_rank,comm_sz,local_n,source,q,r;
    double h,local_int,total_int,local_b,local_a;
    MPI_Init(NULL,NULL);
    MPI_Comm_rank(MPI_COMM_WORLD,&my_rank);
    MPI_Comm_size(MPI_COMM_WORLD,&comm_sz);
    Get_input(my_rank,comm_sz,&a,&b,&n);
    
    h=(b-a)/n;

    q=n/comm_sz;
    r=n%comm_sz;
    
    if(my_rank<r)
    {
        local_n=q+1;
        local_a=a+my_rank*local_n*h;
        local_b=local_a+local_n*h;
    }
    else
    {
        local_n=q;
        local_a=a+my_rank*local_n*h+r*h;
        local_b=local_a+local_n*h;
    }


    local_int=Trap(local_a,local_b,n,h);


    if(my_rank!=0)
    {
        MPI_Send(&local_int,1,MPI_DOUBLE,0,0,MPI_COMM_WORLD);
    }
    else
    {
        total_int=local_int;
        for(source=1;source<comm_sz;source++)
        {
            MPI_Recv(&local_int,1,MPI_DOUBLE,source,0,MPI_COMM_WORLD,MPI_STATUS_IGNORE);
            total_int+=local_int;
        }
        printf("n=%d,a=%.2f b=%.2f area=%f\n",n,a,b,total_int);

    }


    MPI_Finalize();
    return 0;
}
  • 廣播MPI_Bcast(輸入數據a,b,n)
  • 規約MPI_Reduce/MPI_Allreduce(部分積分值求和)
/*
 * mpicc -lm -g -Wall -o ex3_tk1_2 ex3_tk1_2.c && mpiexec -n 5 ./ex3_tk1_2
 */
#include <stdio.h>
#include<math.h>
#include<mpi.h>
double f(double x)

{
    return sin(x);
}
double Trap(double a,double b,double n,double h)
{
    double estimate,x;
    int i;
    estimate=(f(a)+f(b))/2.0;
    for(i=1;i<n;i++)
    {
       x=a+i*h;
       estimate+=f(x);
       
    }
    return estimate*h;
}

void Get_input1(int my_rank,int comm_sz,double* a_p,double* b_p,int* n_p)
{
    
    if(my_rank==0)
    {
        printf("Enter a,b,and n\n");
        scanf("%lf %lf %d",a_p,b_p,n_p);

    }
    MPI_Bcast(a_p,1,MPI_DOUBLE,0,MPI_COMM_WORLD);
    MPI_Bcast(b_p,1,MPI_DOUBLE,0,MPI_COMM_WORLD);
    MPI_Bcast(n_p,1,MPI_INT,0,MPI_COMM_WORLD);

}
int main()
{
    double a=0.0,b=3.0;
    int n=2048,my_rank,comm_sz,local_n;
    double h,local_int,total_int,local_b,local_a;
    MPI_Init(NULL,NULL);
    MPI_Comm_rank(MPI_COMM_WORLD,&my_rank);
    MPI_Comm_size(MPI_COMM_WORLD,&comm_sz);
    Get_input1(my_rank,comm_sz,&a,&b,&n);
    
    h=(b-a)/n;
    local_n=n/comm_sz;

    local_a=a+my_rank*local_n*h;
    local_b=local_a+local_n*h;
    local_int=Trap(local_a,local_b,n,h);

    MPI_Reduce(&local_int, &total_int, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
    //MPI_Allreduce(&local_int, &total_int, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
    
    
    if(my_rank==0)
    {
         printf("n=%d,a=%.2f b=%.2f area=%f\n",n,a,b,total_int);

    }


    MPI_Finalize();
    return 0;
}
 


並行計算和分佈式計算異同點

並行計算(paralel compunting) 一個程序通過多個任務緊密協作來解決某一個問題.
分佈式計算(disributed computing指一個程序需要與其它程序協作來解決某個問題。
並行計算與分佈式計算的區別

  1. 問題的來源與應用領域不同,並行計算主要來自於科學計算領域,而分佈式計算主要來自於商業領域
  2. 系統架構不同:並行計算主要是指在許多核或處理器上進行求解一個問題;而分佈式計算更強調的是跨系統、跨區域進行的協同工作來解決一個問題
  3. 分佈式系統強調的是資源的共享,和併發。

集合通信和點對點通信的不同

  1. 通信子中的所有進程必須調用相同的集合通信函數
  2. 參數必須相容
  3. 參數output_data_p只用在dest_process上,所有進程仍需要傳遞一個與output_data_p相對應的實際參數,即使它的值是NULL
  4. 點對點通信通過標籤和通信子來匹配,而集合通信通過通信子和調用的順序來匹配

派生數據類型

  • 在MPI中,通過同時存儲數據項的類型及它們在內存中的
    相對位置,派生數據類型可以用於表示內存中數據項的任
    意集合。
  • 主要思想:如果發送數據的函數知道數據項的類型及內存
    中數據項集合的相對位置,就可以在數據項被髮送出去之
    前在內存中將數據項聚集起來。
  • 接收函數可以在數據項被接收後將數據項分發到它們在內
    存中正確的目標地址上。
  • 派生數據類型是由一系列的MPI基本數據類型和每個數
    據類型的偏移所組成的

MPI整合多條消息數據的方式:

  • 不同通信函數中的count參數
  • 派生數據類型
  • MPI_Pack/Unpack函數

梯形積分派生數據類型

性能評估

  • 加速比
    T(n,p)parallel=T(n)serial/p+TcostT(n,p)_{parallel}=T(n)_{serial}/p+T_{cost}
    S(n,p)=T(n)serial/T(n,p)parallelS(n,p)=T(n)_{serial}/T(n,p)_{parallel}
  • 效率
    E(n,p)=S(n,p)/pE(n,p)=S(n,p)/p

線性加速比相當於並行效率p/p=1

第五章(openmp)

OpenMP的編譯運行(共享內存系統)
重點是理解和使用常見的指令、子句和函數的功能,能夠熟練應用
重點講解了幾個實例:梯形積分(多種並行形式) ,特別注意for循歡的並行

指令

  • paralle指令:用在一個代碼段之前,表示這段代碼將被多個線程並行執行。
  • for指令:使循環被多個線程並行執行;
  • parallef for指令:循環的代碼被多個線程並行執行。
  • atomic指令: 實現互斥訪問最快的方法。
  • critical指令: 保護臨界區,實現互厲訪問。
  • barrier指令 : 顯式路障,線程組中的線程都達到這個路障,才繼續往下執行。

子句

  • num threads:用來指定執行之後的代碼塊的線程數目
  • reduction:用來對一個或多個參數條目指定一個操作符
  • default:用來允許用戶控制並行區域中變量的共享屬性
  • private:用來聲明一個或多個變量是私有變量
  • shared:用來聲明一個或多個變量是共享變量
  • schedule:調度任務實現分配給線程任務的不同劃分方式

函數

  • omp_get_num_threads() // 返回當前並行區域中的活動線程1쐦.

  • omp_get_thread_num() //返回線程號。

  • omp_get_wtime() //計算OpenMP並行程序花費時間

  • omp_set num_threads() //設置線程的數量

  • void omp_ init_lock(omp_ lock_ t*lock) //初始化鎖

  • void omp destroy_lock(omp_ lock t*lock) //銷燬鎖

  • void omp_set_lock(omp_lock_t* lock) //嘗試獲得鎖

  • void omp_unset_lock(omp_ lock_ t* lock) /釋放鎖

openmp實例

奇偶排序帶openmp簡單優化

openmp(三)π值估計

臨界區

生產者消費者隊列程序

定義

臨界區指的是一個訪問共用資源(例如:共用設備或是共用存儲器)的程序片段,而這些共用資源又無法同時被多個線程訪問的特性。

如何保護

  • critical指令
  • 命名的critcal指令
  • atomic指令
  • 簡單鎖
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章