实验一 Linux下MPI的hello和greetings程序设计

  • 熟悉Linux环境的MPICH并行计算库

列出当前目录中的所有文件或目录:ls

删除文件或空目录:rm

上传文件:rz

编译:mpicc -g -Wall -o helloc 
执行:mpiexec -n 4 ./hello

二.HELLO WORLD并行程序设计

(一)程序源码:

(1)源码:

#include <mpi.h>

#include <stdio.h>

int main(int argc, char** argv) {

    MPI_Init(NULL, NULL);

    int wsize;//单词长度

    MPI_Comm_size(MPI_COMM_WORLD, &wsize);

    int wrank;

    MPI_Comm_rank(MPI_COMM_WORLD, &wrank);

    char processor_name[MPI_MAX_PROCESSOR_NAME];//处理器名称

    int nlen;

    MPI_Get_processor_name(processor_name, &nlen);

    printf("Hello world from processor %s, rank %d out of %d processors\n",

           processor_name, wrank, wsize);

    MPI_Finalize();

}

(2)源码截屏:

  1. 关键函数分析:

a. MPI_Init: 初始化MPI环境。此函数需要在使用任何MPI函数之前调用。接受两个参数,可以传入命令行参数,但本例中传入了NULL,因为不需要特殊参数。

b. MPI_Comm_size: 获取通信组中的进程数量。此函数接受两个参数:一个通信器(通常是MPI_COMM_WORLD,表示所有可用的MPI进程)和一个指向整数的指针,用于存储进程数量。在本例中,wsize被设置为进程数量。

c. MPI_Comm_rank: 获取当前进程的等级(唯一标识)。同样接受一个通信器和一个指向整数的指针,用于存储等级。在本例中,wrank被设置为当前进程的等级。

d. MPI_Get_processor_name: 获取处理器名称。它接受一个字符数组和一个指向整数的指针,用于存储名称长度。在本例中,processor_name存储处理器名称,nlen存储名称长度。

e. printf: 标准C库函数,用于输出。在本例中,输出处理器名称、当前进程等级和进程总数。

f. MPI_Finalize: 结束MPI环境。在使用完MPI函数后调用,以清理资源。

  1. 代码实现的整体逻辑:

本程序是一个简单的MPI程序,用于演示如何使用MPI函数初始化MPI环境、获取通信组中的进程数量、获取当前进程的等级以及处理器名称。程序首先初始化MPI环境,然后获取通信组中的进程数量和当前进程的等级。接着,获取处理器名称并输出相关信息。最后,程序结束MPI环境。

(二)程序编译

mpicc -g -Wall -o hello hello.c

(三)运行结果

mpiexec -n 4 ./hello

  • 实验心得:

(1)程序编写技巧:

a. 调用顺序:在使用MPI函数时,需要注意调用顺序。首先需要调用MPI_Init初始化环境,最后调用MPI_Finalize结束环境。在这两个函数之间,可以调用其他MPI函数。

b. 传递指针:注意MPI_Comm_size、MPI_Comm_rank和MPI_Get_processor_name函数需要传递指向整数的指针来获取结果。避免传递错误的指针类型,以免导致未定义行为。

c. 使用MPI_COMM_WORLD:在大多数情况下,我们可以使用MPI_COMM_WORLD作为通信器。这是一个全局通信器,表示所有可用的MPI进程。

d. 并行思维:编写MPI程序时,需要注意程序将在多个进程中并行运行。尽管本例非常简单,但在处理更复杂的问题时,需要考虑如何将问题拆分成子任务,并在进程之间传递数据。

函数使初始化MPI:通过调用MPI_Init(NULL, NULL)函数来初始化MPI。在参数中,使用NULL表示使用默认的命令行参数。

获取进程数量:使用MPI_Comm_size(MPI_COMM_WORLD, &wsize)函数获取通信域MPI_COMM_WORLD中的进程数量,并将结果存储在wsize变量中。

获取进程排名:使用MPI_Comm_rank(MPI_COMM_WORLD, &wrank)函数获取当前进程在通信域MPI_COMM_WORLD中的排名,并将结果存储在wrank变量中。

获取处理器名称:使用MPI_Get_processor_name(processor_name, &nlen)函数获取当前进程的处理器名称,并将结果存储在processor_name数组中。nlen变量用于指定处理器名称的最大长度。

输出信息:使用printf函数将进程的信息输出到标准输出。这里使用了processor_name(处理器名称)、wrank(进程排名)和wsize(进程数量)等变量。

结束MPI:在程序的最后通过调用MPI_Finalize()函数来结束MPI。用技巧:

可能出现问题的地方:

编译和链接MPI库:在编译代码时,需要使用MPI编译器和MPI库。例如,使用mpicc编译器编译代码时,可以使用以下命令:mpicc your_code.c -o executable_name -lmpi。

并行环境设置:在运行该程序时,需要在并行环境中执行,以便多个进程能够正常工作。具体如何设置并行环境取决于使用的MPI实现和集群管理工具。

并行调试:在并行程序中,可能会遇到与进程同步、通信和并发相关的问题。调试并行程序可能比调试串行程序更具挑战性。可以使用MPI提供的调试工具,如mpiexec, mpirun等,以及其他调试技术来帮助解决问题。

命令行参数:该代码示例中使用了MPI_Init(NULL, NULL)来指定使用默认的命令行参数。如果需要在命令行中传递参数给MPI程序,可以修改这部分代码,使用命令行参数进行初始化。

错误处理:MPI函数返回的错误代码可以用于检测并处理MPI调用中的错误。在实际的MPI程序中,通常需要对MPI函数的返回值进行检查,并根据错误码采取相应的错误处理措施。

心得:

在HELLO WORLD并行程序设计的实验中,我初步了解了并行计算和MPI(Message Passing Interface)的概念和使用方法。通过这个实验,我学会了如何使用MPI库中的函数和数据类型,实现多个进程之间的消息传递和同步,以及基本的并行计算任务的分发和处理。

在实现过程中,我遇到了一些问题,例如程序运行时出现错误或崩溃等,这时我通过调试程序和查看MPI函数文档等方式来解决问题。同时,我还学会了如何在命令行终端中使用MPI命令行工具来启动和管理MPI程序。

通过这个实验,我更深入地理解了并行计算和MPI编程的基本原理和技术,也提高了我的编程和调试能力。这将对我的未来学习和研究产生积极的影响。

三.Greetings并行程序设计(非根进程发送消息,根进程接收)

(一)程序源码:

(1)源码:

#include <stdio.h>

#include <string.h>

#include <mpi.h>

const int MAX_STRING = 100;

int main(void) {

        char greeting[MAX_STRING];

        int sz;

        int rank;

        MPI_Init(NULL, NULL);

        MPI_size(MPI_WORLD, &sz);

        MPI_rank(MPI_WORLD, &rank);

        if(rank != 0) {

                sprintf(greeting, "Greeting from process %d of %d!", rank, sz);

                MPI_Send(greeting, strlen(greeting) + 1, MPI_CHAR, 0, 0, MPI_WORLD);

        }else {

                printf("Greetings from process %d of %d!\n", rank, sz);

                for(int i = 1; i < sz; i++) {

                        MPI_Recv(greeting, MAX_STRING, MPI_CHAR, i, 0, MPI_WORLD, MPI_STATUS_IGNORE);

                        printf("%s\n", greeting);

                }

        }

        MPI_Finalize();

        return 0;

}

(2)源码截屏:

  1. 关键函数分析:

a. MPI_Init: 初始化MPI环境。此函数需要在使用任何MPI函数之前调用。接受两个参数,可以传入命令行参数,但本例中传入了NULL,因为不需要特殊参数。

b. MPI_size: 获取通信组中的进程数量。此函数接受两个参数:一个通信器(通常是MPI_WORLD)和一个指向整数的指针,用于存储进程数量。在本例中,sz被设置为进程数量。

c. MPI_rank: 获取当前进程的等级(唯一标识)。同样接受一个通信器和一个指向整数的指针,用于存储等级。在本例中,rank被设置为当前进程的等级。

d. sprintf: 标准C库函数,将格式化字符串写入字符数组。在本例中,将进程信息写入greeting字符数组。

e. MPI_Send: 发送消息给其他进程。接受六个参数:要发送的数据缓冲区、数据数量、数据类型、目标进程等级、消息标签和通信器。在本例中,非0等级的进程将greeting消息发送给0等级的进程。

f. MPI_Recv: 接收其他进程发送的消息。接受七个参数:接收缓冲区、数据数量、数据类型、源进程等级、消息标签、通信器和一个状态对象。在本例中,0等级的进程接收其他进程发送的greeting消息。

g. printf: 标准C库函数,用于输出。在本例中,输出进程信息。

h. MPI_Finalize: 结束MPI环境。在使用完MPI函数后调用,以清理资源。

  1. 代码实现的整体逻辑:

本程序是一个简单的MPI程序,用于演示进程间的通信。程序首先初始化MPI环境,然后获取通信组中的进程数量和当前进程的等级。对于非0等级的进程,它们将进程信息发送给0等级的进程。对于0等级的进程,它首先输出自己的进程信息,然后接收并输出其他进程发送的进程信息。最后,程序结束MPI环境。

(二)程序编译

mpicc -g -Wall -o greeting greeting.c

  • 运行结果

mipiexec -n 4 ./greeting

  • 实验心得

程序编写技巧:

a. 并行思维:编写MPI程序时,需要注意程序将在多个进程中并行运行。在处理更复杂的问题时,需要考虑如何将问题拆分成子任务,并在进程之间传递数据。

b. 正确使用MPI_Send和MPI_Recv:注意在发送和接收消息时,要保证数据类型、数据数量、源/目标进程等级和消息标签相匹配。不正确的参数可能导致错误的结果或程序阻塞。

c. 注意数组大小:在本例中,字符数组greeting的大小被设置为MAX_STRING,这需要确保足够大以容纳进程信息。如果数组太小,可能会导致缓冲区溢出,从而引发未定义行为。

d. 避免死锁:在使用MPI_Send和MPI_Recv进行进程间通信时,要注意避免死锁的发生。死锁通常发生在多个进程相互等待对方完成操作时。为了避免死锁,可以使用其他的通信模式,如非阻塞通信(MPI_Isend和MPI_Irecv)或集体通信操作(如MPI_Bcast、MPI_Scatter和MPI_Gather)。

e. 使用MPI_STATUS_IGNORE:在使用MPI_Recv时,如果不关心接收到的消息的状态信息,可以使用MPI_STATUS_IGNORE作为状态参数。这可以简化代码,避免不必要的状态变量声明。

f. 掌握MPI常见数据类型:熟悉MPI提供的各种预定义数据类型,如MPI_INT、MPI_FLOAT、MPI_DOUBLE和MPI_CHAR等,以便在发送和接收数据时正确设置数据类型。

g. 保持代码简洁:在编写MPI程序时,尽量保持代码简洁明了,以便于理解和维护。如本例中,通过对非0等级进程和0等级进程分别处理,使得代码逻辑清晰。

通过编写和分析这段代码,可以加深对MPI编程的理解,掌握进程间通信的基本方法,并学习到一些避免常见错误和提高代码质量的技巧。

函数使用技巧

  1. 初始化MPI:通过调用MPI_Init(NULL, NULL)函数来初始化MPI。在参数中,使用NULL表示使用默认的命令行参数。
  2. 获取进程数量和排名:使用MPI_Comm_size(MPI_COMM_WORLD, &sz)函数获取通信域MPI_COMM_WORLD中的进程数量,并使用MPI_Comm_rank(MPI_COMM_WORLD, &rank)函数获取当前进程在通信域中的排名。
  3. 发送消息:在排名不为0的进程中,使用MPI_Send函数将一条包含进程排名的问候消息发送到排名为0的主进程。其中,strlen(greeting) + 1表示消息的长度,MPI_CHAR表示消息的数据类型。
  4. 接收消息:在主进程(排名为0)中,使用MPI_Recv函数接收从其他进程发送的问候消息。MPI_STATUS_IGNORE表示忽略接收状态。
  5. 打印消息:在主进程中,使用printf函数将接收到的问候消息打印出来。
  6. 结束MPI:在程序的最后通过调用MPI_Finalize()函数来结束MPI。

可能出现问题的地方

  1. 函数名称错误:代码中使用了MPI_sizeMPI_rank函数来获取进程数量和排名,正确的函数名称应该是MPI_Comm_sizeMPI_Comm_rank
  2. MPI_WORLD的正确写法:代码中使用了MPI_WORLD,正确的写法应该是MPI_COMM_WORLD,表示通信域。
  3. 字符串溢出:在发送消息时,使用strlen(greeting) + 1作为消息的长度。请确保发送的消息不会超过MAX_STRING定义的最大长度。
  4. 接收消息的循环:在主进程中使用循环来接收其他进程发送的消息。在循环中,使用MPI_Recv函数接收消息,并使用MPI_CHAR作为消息的数据类型。确保接收到足够的消息来避免阻塞。
  5. 错误处理:MPI函数返回的错误代码可以用于检测并处理MPI调用中的错误。在实际的MPI程序中,通常需要对MPI函数的返回值进行检查,并根据错误码采取相应的错误处理措施。

心得体会

在Greetings并行程序设计的实验中,我主要学习了如何使用MPI(Message Passing Interface)在多个进程之间进行消息传递。这个实验要求非根进程向根进程发送消息,而根进程接收并打印出来。

通过这个实验,我深刻理解了MPI中的通信机制和编程模式,比如点对点通信、广播、归约等。同时,我也学会了如何使用MPI库中的各种函数来完成消息传递和处理,例如MPI_Send和MPI_Recv等函数。

在实现非根进程发送消息和根进程接收消息的过程中,我遇到了一些问题,例如发送和接收的数据类型不匹配、发送的消息大小超过了接收缓冲区的大小等。我解决这些问题的方法是调试程序、查看MPI函数的文档和资料,并根据错误信息进行排除。

总的来说,这个实验让我更加深入地了解了MPI的使用和通信模型,也提高了我的调试技能和问题解决能力。在今后的研究和工作中,我相信这些经验和技能将会对我有很大的帮助。

  • 17
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值