MPI的MPI并行编程

MPI的MPI并行编程,第1张

并行编程模式

对等模式—程序的各个部分地位相同,功能和代码基本一致,只是处理的数据或对象不同;主从模式—程序通信进程之间的一种主从或依赖关系 。

点对点通信模式

阻塞—发送完成的数据已经拷贝出发送缓冲区,即发送缓冲区可以重新分配使用,阻塞接受的完成意味着接收数据已经拷贝到接收缓冲区,即接收方已可以使用。非阻塞—在必要的硬件支持下,可以实现计算和通信的重叠。4种通信模式:标准通信模式、缓存通信模式、同步通信模式、就绪通信模式 。

组通信

一个特定组内所有进程都参加全局的数据处理和通信 *** 作 。

功能:通信—组内数据的传输;同步—所有进程在特定的点上取得一致;计算—对给定的数据完成一定的 *** 作 。

类型:1)数据移动:广播(mpi bcast) 收集(mpi gather) 散射(mpi scater)组收集(mpi all gather)全交换(all to all);2)聚集:规约(mpi reduce)将组内所有的进程输入 缓冲区中的数据按,定 *** 作OP进行运算,并将起始结果返回到root进程的接收缓冲区扫描(mpi scan)要求每一个进程对排在它前面的进程进行规约 *** 作,结果存入自身的输出缓冲区;3)同步:路障(mpi barrier)实现通信域内所有进程互相同步,它们将处于等待状态,直到所有进程执行它们各自的MPI-BARRIER调用 。

虚拟机CentOS中配置MPI多节点并行计算完整版,这是一个新手教程,从一个空白的CentOS到能正常运行的基础环境,尽量详细写了每个步骤

我的环境如下:

宿主机:Windows 10 Pro

虚拟机:VMware 12 + CentOS 6.6

一、 虚拟机网络配置实现多节点互访

硬件资源有限,所以我只开了两台虚拟机均为 Centos 6.6。

1.修改网卡配置文件

/etc/sysconfig/network-scripts/ifcfg-eth0 将其中内容修改如下:

DEVICE=eth0

HWADDR=00:0C:29:A0:3A:CA

TYPE=Ethernet

UUID=bf583745-f834-451c-890c-39821a590543

ONBOOT=yes

NM_CONTROLLED=yes

BOOTPROTO=static

IPADDR=192.168.207.128

NETMASK=255.255.255.0

DNS1=192.168.207.2

将每一台虚拟机都如此配置,IPADDR是ip地址,不要重复

2.修改网关及主机名

/etc/sysconfig/network

ETWORKING=yes

HOSTNAME=node1

NTPSERVERARGS=iburst

GATEWAY=192.168.207.2

一些文章中提到还需要修改VMware的设置(关闭本地DHCP服务),实际上,Centos 中设置好后就不需要动VMware的设置了

二、 在虚拟机中添加同名用户

在不同节点中需要有同名的用户以方便免密码SSH连接

node1和node2中以root用户执行下列命令

# useradd mpiuser

三、 实现免密码SSH登陆

当前节点为node1,用户为mpiuser,工作目录为 ~ 家目录

1.生成 SSH 私钥对

$ ssh-keygen -t rsa 一路回车就好

2.进入 .ssh 目录

$ cd ./.ssh

3.生成authorized_keys文件

$ cp id_rsa.pub authorized_keys

4.回到mpiuser的家目录

$ cd ~

5.建立本身的信任连接

$ ssh node1

6.设置 node2

$ ssh-keygen -t rsa 生成.ssh文件夹

$ scp node1:~/.ssh/* ./ 拷贝node1上的.ssh文件夹到node2

# scp node1:/etc/hosts /etc/hosts 拷贝node1上的hosts文件到node2,可能需要root权限

$ ssh node2

$ ssh node1

7.如果还有其他节点,设置方法同node2

设置成功后,在任意节点执行SSH连接其他节点都不需要密码,注意必需每个节点都有mpiuser这个用户,之所以使用一个普通用户,是为了避免root执行mpi程序时遇到风险。在一些mpi实现中,也不允许以root用户执行。

四、 配置MPI运行环境

1. 下载 mpich

官方网站 http://www.mpich.org 下载其中的mpich-3.2.tar.gz

2. 解压并进入目录

# tar xvf mpich-3.2.tar.gz

# cd mpich-3.2

# ./configure –prefix=/usr/local/mpich

# make

# make install

3. 设置环境变量,修改 /etc/profile ,加入下面几行代码

PATH=$PATH:/usr/local/mpich/bin

MANPATH=$MANPATH:/usr/local/mpich/man

export PATH MANPATH

4. 使刚刚的修改生效

# source /etc/profile

5. mpi多节点配置

新建一个配置文件

# touch /usr/local/mpich/servers

修改其中内容为:

node1:2 #在node1上运行两个进程

node2:2

如果有多个节点继续往下写

五、 单节点测试

将源码包中的 examples 拷贝到 mpich 的安装目录

# cp -r ./examples /usr/local/mpich/

用示例程序进行测试

$ mpirun -np 4 /usr/local/mpich/examples/cpi

Process 1 of 4 is on node1

Process 0 of 4 is on node1

Process 3 of 4 is on node1

Process 2 of 4 is on node1

pi is approximately 3.1415926544231239, Error is 0.0000000008333307

wall clock time = 0.081049

得到如图的回显结果证明mpich的运行环境正常

六、 多节点测试

$ mpiexec -np 4 -f /usr/local/mpich/servers /usr/local/mpich/examples/cpi

Process 1 of 4 is on node1

Process 0 of 4 is on node1

Process 3 of 4 is on node2

Process 2 of 4 is on node2

pi is approximately 3.1415926544231239, Error is 0.0000000008333307

wall clock time = 0.139722

如上,在两个节点上分别运行了两个进程,配置过程到此结束

在 上一篇 中我们介绍了 MPI-3 中共享内存 *** 作,下面我们将介绍 MPI 中多线程的使用,以助于我们理解 MPI-3 中引进的线程安全的 Mprobe *** 作(将在 下一篇 中介绍)。

通常 MPI 中大多数 *** 作的基本实体是进程,但是MPI 进程中可以执行多个线程。

进程是 *** 作系统动态执行的基本单元,在传统的 *** 作系统中,进程既是基本的分配单元,也是基本的执行单元。通常在一个进程中可以包含若干个线程,一个进程中至少有一个线程。线程可以利用进程所拥有的资源,在引入线程的 *** 作系统中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位,由于线程比进程粒度更小,基本上不拥有系统资源,故对它的调度所付出的开销就会小得多,能更高效的提高系统多个程序间并发执行的程度。

进程和线程的主要差别在于它们是不同的 *** 作系统资源管理方式。进程有独立的地址空间,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程没有独立的地址空间。

MPI 进程中可以执行多个线程,同一个进程的多个线程具有均等的机会参与该进程的 MPI 通信。

某些情况下在 MPI 中使用多线程能够提供很大的方便,如:

在单线程的情况下,一个进程无法安全地执行到其自身的阻塞点到点通信。采用多线程,则可使同一个进程的两个线程分别执行阻塞发送和阻塞接收而不至于死锁。

MPI 要求 MPI_Init 和 MPI_Finalize 调用必须在相同线程内配对执行,执行了这两个 *** 作的线程被称作主线程(main thread),主线程的 MPI_Finalize 调用必须在所有其它线程都执行完 MPI 相关的通信、I/O 和其它 MPI *** 作之后执行。

线程安全性是指多个线程可以同时执行消息传递的相关调用而不会相互影响。

MPI被设计为线程安全的,但应用程序自身负责维护多线程安全,一个简单的办法是在不同线程使用不同的通信子对象,这样可实现线程间 *** 作的互不干扰。MPI 的大多数 *** 作都满足线程安全性的条件,但是也有例外,如在多线程中使用 MPI.Comm.Probe 或 MPI.Comm.Iprobe 来确定一个消息的来源和大小,然后接收该消息的 *** 作就不是线程安全的,不过 MPI-3 提供了线程安全的版本 MPI.Comm.Mprobe 和 MPI.Comm.Improbe,这些将在 下一篇 中介绍。另外 MPI.File.Seek 也不是线程安全的,但是可以用线程安全的显式偏移文件 *** 作函数,如 MPI.File.Read_at 等来替代其 *** 作。

该函数除了实施正常由 MPI.Init 执行的初始化之外,还负责初始化 MPI 多线程执行环境。 reauired 参数指出所要求的多线程支持程度,可能的取值如下:

这些常数都是整数,并且在数值上是从小到大的。

该函数返回 MPI 环境实际支持的多线程级别。

MPI.COMM_WORLD 中不同进程可分别设置不同的线程支持级别。多线程 MPI 中调用 MPI.Init 的实际效果等价于用 MPI.THREAD_SINGLE 调用 MPI.Init_thread。

注意 :使用 mpi4py 时,MPI.Init 和 MPI.Init_thread 在从 mpi4py 包中 import MPI 时会被自动调用,因此一般不用在程序中显式调用。mpi4py 中 MPI.Init_thread 默认 required 的线程级别是 MPI.THREAD_MULTIPLE,但实际得到的线程级别由 MPI 环境给出。如果想要手动控制 MPI 程序的初始化或者设置 MPI 线程级别,可以在 import MPI 之前先 import rc 模块,并设置 rc.initialize = False,然后手动初始化,或者设置 rc.thread_level 为需要的级别("multiple", "serialized", "funneled" 或 "single")。

返回 MPI 环境支持的线程级别。

判断调用该函数的线程是否为主线程,如果是,返回 True,否则返回 False。

MPI 并没有提供创建线程的函数或方法,创建线程需由其它的工具来完成,这就保证了 MPI 程序可以使用任何与 MPI 实现兼容的线程,而不局限于某一种特定的线程。比较广泛使用的线程工具有 Pthreads 库和 OpenMP 库。

在 Python 中则可以使用 thread 模块(Python 3 中被命名为 _thread),但是更常用的是建立在其之上的更易用更高级别的线程模块 threading 。下面对 threading 模块作简要的介绍。

threading 模块提供了若干函数和对象,这里主要介绍 threading.Thread 对象。该对象表示一个单独运行的线程活动,可以通过两种方式来创建和运行一个单独的线程:为其传递一个可以被调用的对象;或者继承该类并重载 run() 方法。一般在子类中只能重载 __init__() 和 run() 方法,其它方法都不应该被重载。

当一个线程被创建后,必须使用 start() 方法来启动该线程,该方法会在内部调用 run() 方法。

一个线程被启动后,它的状态会是 "alive",直到该线程的 run() 方法运行停止(不管是正常结束还是异常中止)。可以用 is_alive() 方法来测试该线程是否 "alive"。

其它线程可以调用一个线程的 join() 方法,此 *** 作会阻塞该调用线程直到被调用线程停止。

每个线程都有一个名称,如果没有设置,系统会为其分配一个默认的名称。可以在构造一个线程时为其传递一个指定的名称,或者在运行过程中通过 name 属性动态改变。

一个线程可以被标记为守护线程(daemon),守护线程可以一直运行而不阻塞主程序退出。如果一个服务无法用一种容易的方法来中断线程,或者希望线程工作到一半时中止而不会损失或破坏数据,对此服务,使用守护线程就很有用。可以通过设置线程的 daemon 属性来标记一个线程为守护线程。默认情况下线程不作为守护线程。

程序的初始线程为主线程,主线程不是守护线程。

初始化一个线程目标。 group 初始应该设置为 None,目前没有作用,为今后的扩展所保留, target 为一个可被调用的对象, name 如果非 None,设置线程的名称,默认名称为 "Thread-N",N 为一个数字, args 和 kwargs 为其它参数。

如果子类要重载此构造方法,必须首先调用基类的构造方法(Thread.__init__()),然后再做其它事情。

启动该线程。一个线程只能至多调用一次该方法,否则会抛出 RuntimeError 异常。

线程的活动或工作。可以在子类中重载该方法以完成需要的工作。

等待线程停止。默认情况下调用该方法的线程会无限阻塞直到被调用线程停止,但是如果 timeout 参数设置为一个浮点数,则只会等待 timeout 秒就返回,无论被调用线程是否中止。该方法总是返回 None,因此必须调用 is_alive() 方法来确定该调用的返回是由于被调用线程停止还是由于等待了 timeout 时间。

可以调用一个线程的 join() 方法多次。

返回该线程是否 "alive"。

线程的名称,为一个字符串。

线程的标识符,为一个非 0 整数。

一半布尔值表示线程是否为守护线程。

下面给出使用例程。

运行结果如下:

以上介绍了 MPI 中多线程的使用,在 下一篇 中我们将介绍 MPI-3 中线程安全的 Mprobe。


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/11793021.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-18
下一篇 2023-05-18

发表评论

登录后才能评论

评论列表(0条)

保存