高性能计算复习

第一二章

高性能计算概念

高性能计算(High performance computing,缩写HPC) 指通
常使用很多处理器(作为单个机器的一部分)或者某一集群
中组织的几台计算机(作为单个计算资源操作)的计算系统
和环境

性能衡量单位

floats

KMGTP
369121518
百万十亿万亿千万亿百亿亿

并行硬件

  • 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 ) p a r a l l e l = T ( n ) s e r i a l / p + T c o s t T(n,p)_{parallel}=T(n)_{serial}/p+T_{cost} T(n,p)parallel=T(n)serial/p+Tcost
    S ( n , p ) = T ( n ) s e r i a l / T ( n , p ) p a r a l l e l S(n,p)=T(n)_{serial}/T(n,p)_{parallel} S(n,p)=T(n)serial/T(n,p)parallel
  • 效率
    E ( n , p ) = S ( n , p ) / p E(n,p)=S(n,p)/p E(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指令
  • 简单锁
  • 5
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
好的,让我来回答这个问题。首先,我们可以使用MPI_Scatter函数将数组分发到各个进程中。然后,进程0可以使用MPI_Gather函数将所有进程中的数据收集到一个数组中。下面是一个示例代码: ``` int main(int argc, char** argv) { int comm_sz, my_rank; int n = 16; int* arr = NULL; int* recv_buf = NULL; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &comm_sz); MPI_Comm_rank(MPI_COMM_WORLD, &my_rank); if (my_rank == 0) { arr = (int*)malloc(n * sizeof(int)); for (int i = 0; i < n; i++) { arr[i] = i; } } int local_n = n / comm_sz; int* local_arr = (int*)malloc(local_n * sizeof(int)); MPI_Scatter(arr, local_n, MPI_INT, local_arr, local_n, MPI_INT, 0, MPI_COMM_WORLD); if (my_rank == 0) { recv_buf = (int*)malloc(n * sizeof(int)); } MPI_Gather(local_arr, local_n, MPI_INT, recv_buf, local_n, MPI_INT, 0, MPI_COMM_WORLD); if (my_rank == 0) { for (int i = 0; i < n; i++) { printf("%d ", recv_buf[i]); } printf("\n"); free(arr); free(recv_buf); } free(local_arr); MPI_Finalize(); return 0; } ``` 现在,让我们来看一下如何使用树形结构的通信来实现MPI_Gather。假设我们有8个进程,我们可以使用以下树形结构: ``` 0 / \ 1 2 / \ / \ 3 4 5 6 | 7 ``` 在这个树形结构中,进程0是根节点,每个进程都有一个父节点和零个或多个子节点。我们可以使用递归的方式来实现树形结构的通信。具体来说,进程0将收集来自其子节点的数据,并将它们存储在一个缓冲区中。然后,进程0将这个缓冲区发送给其父节点。父节点将收集来自其子节点的数据,并将它们存储在一个缓冲区中。然后,父节点将这个缓冲区发送给其父节点,以此类推,直到根节点收集了所有数据。 下面是一个示例代码: ``` int gather_tree(int* send_buf, int send_count, int* recv_buf, int recv_count, int root, MPI_Comm comm) { int comm_sz, my_rank; MPI_Comm_size(comm, &comm_sz); MPI_Comm_rank(comm, &my_rank); if (my_rank == root) { memcpy(recv_buf, send_buf, send_count * sizeof(int)); } int parent = (my_rank - 1) / 2; int left_child = 2 * my_rank + 1; int right_child = 2 * my_rank + 2; if (left_child < comm_sz) { gather_tree(send_buf, send_count, recv_buf, recv_count, root, MPI_Comm_split(comm, left_child, my_rank, &comm)); } if (right_child < comm_sz) { gather_tree(send_buf, send_count, recv_buf, recv_count, root, MPI_Comm_split(comm, right_child, my_rank, &comm)); } if (my_rank != root) { MPI_Send(recv_buf, recv_count, MPI_INT, parent, 0, comm); } return 0; } ``` 在这个示例代码中,我们使用MPI_Comm_split函数来创建一个新的通信子集,其中只有一个进程是父进程,其余进程都是子进程。然后,我们使用递归的方式来实现树形结构的通信。注意,我们使用MPI_Send函数来发送数据,而不是MPI_Bcast函数,因为我们需要将数据发送给父节点,而不是所有进程。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值