如何以编程方式检测内核数量并使用所有内核运行MPI程序

如何以编程方式检测内核数量并使用所有内核运行MPI程序,mpi,Mpi,我不想使用mpiexec-n4./a.out在我的Corei7处理器(有4个内核)上运行我的程序。相反,我想运行/a.out,让它检测内核的数量并启动MPI以运行每个内核的进程 这个问答让我使用了mpiexec 我想避免使用mpiexec的原因是,我的代码注定要成为我正在处理的更大项目中的一个库。较大的项目有一个GUI,用户将开始长时间的计算,这些计算将调用我的库,而我的库又将使用MPI。用户界面和计算代码之间的集成不是微不足道的。。。因此,启动外部进程并通过套接字或其他方式进行通信不是一种选择

我不想使用
mpiexec-n4./a.out
在我的Corei7处理器(有4个内核)上运行我的程序。相反,我想运行
/a.out
,让它检测内核的数量并启动MPI以运行每个内核的进程

这个问答让我使用了
mpiexec

我想避免使用mpiexec的原因是,我的代码注定要成为我正在处理的更大项目中的一个库。较大的项目有一个GUI,用户将开始长时间的计算,这些计算将调用我的库,而我的库又将使用MPI。用户界面和计算代码之间的集成不是微不足道的。。。因此,启动外部进程并通过套接字或其他方式进行通信不是一种选择。一定是图书馆的电话


这可能吗?如何操作?

您可以使用示例解决方案获取CPU数量,然后通过调用启动MPI进程。但是,您需要有一个单独的可执行文件。

一般来说,这是一件非常重要的事情。此外,几乎没有任何可移植的解决方案不依赖于某些MPI实现细节。下面是一个示例解决方案,它可以与开放MPI一起使用,也可以与其他通用MPI实现(MPICH、Intel MPI等)一起使用。它涉及到第二个可执行文件或原始可执行文件直接调用库的方法,提供了一些特殊的命令行参数。事情是这样的

假设原始可执行文件只是作为
/a.out
启动的。调用库函数时,它将调用初始化MPI的
MPI_Init(NULL,NULL)
。由于可执行文件不是通过
mpiexec
启动的,因此它会返回到所谓的单例MPI初始化,即创建一个由单个进程组成的MPI作业。要执行分布式计算,您必须启动更多的MPI进程,在一般情况下,这就是事情变得复杂的原因

MPI支持动态流程管理,其中一个MPI作业可以启动第二个MPI作业,并使用内部通讯器与之通信。当第一个作业调用
MPI\u Comm\u spawn
MPI\u Comm\u spawn\u multiple
时会发生这种情况。第一个用于启动对所有MPI列组使用相同可执行文件的简单MPI作业,而第二个用于启动混合不同可执行文件的作业。两者都需要关于在何处以及如何启动流程的信息。这来自所谓的MPI宇宙,它不仅提供有关已启动进程的信息,还提供有关动态启动进程的可用插槽的信息。宇宙是由
mpiexec
或其他一些启动机制构建的,这些启动机制采用的是一个主机文件,其中包含节点列表和每个节点上的插槽数。在缺少此类信息的情况下,某些MPI实现(包括Open MPI)将在与原始文件相同的节点上启动可执行文件
MPI\u Comm\u spawn[\u multiple]
有一个
MPI\u Info
参数,可用于提供包含实现特定信息的键值列表。Open MPI支持
add hostfile
键,该键可用于指定生成子作业时要使用的主机文件。这有助于,例如,允许用户通过GUI指定用于MPI计算的主机列表。但让我们集中讨论没有提供此类信息的情况,OpenMPI只是在同一台主机上运行子作业

假设工作程序可执行文件名为
worker
。或者,如果使用一些特殊的命令行选项调用,原始可执行文件可以充当辅助程序,例如,
-worker
。如果您希望总共使用
N
进程执行计算,则需要启动
N-1
worker。这很简单:

(单独的可执行文件)

MPI\u通信子通信;
MPI_Comm_spawn(“./工作者”,MPI_ARGV_NULL,N-1,MPI_INFO_NULL,0,
MPI_COMM_SELF和child_COMM、MPI_errcode_IGNORE);
(相同的可执行文件,带有一个选项)

MPI\u通信子通信;
char*argv[]={“-worker”,NULL};
MPI命令产卵(“./a.out”),argv,N-1,MPI信息为空,0,
MPI_COMM_SELF和child_COMM、MPI_errcode_IGNORE);
如果一切顺利,
child\u comm
将被设置为可用于与新作业通信的内部通讯器的句柄。由于内部通讯器的使用有点棘手,且父子作业分工需要复杂的程序逻辑,因此可以简单地将内部通讯器的两侧合并成一个“大世界”通讯器,取代
MPI\u COMM\u world
。在家长方面:

MPI_Comm bigworld;
MPI\u内部通信\u合并(子通信、0和大世界);
在儿童方面:

MPI\u Comm parent\u Comm,bigworld;
MPI\u获取\u父项(&parent\u comm);
MPI\u内部通信合并(父通信、1和大世界);
合并完成后,所有进程都可以使用
bigworld
而不是
MPI\u COMM\u WORLD
进行通信。请注意,子作业不会与父作业共享其
MPI\u COMM\u WORLD

总而言之,这里有一个完整的功能示例,有两个单独的程序代码

main.c

#包括
#包括
内部主(空)
{
MPI_Init(NULL,NULL);
printf(“[main]产卵工人…\n”);
MPI_通信子通信;
MPI_Comm_spawn(“./工作者”,MPI_ARGV_NULL,2,MPI_INFO_NULL,0,
MPI_COMM_SELF和child_COMM、MPI_errcode_IGNORE);
MPI_Comm bigworld;
MPI\u内部通信\u合并(子通信、0和大世界);
整数大小、等级;
MPI通信等级(大世界和等级);
MPI_通信大小(大世界和大小);
printf(“[main]用%d个等级创建的大世界\n”,大小);
//进行一些计算
int data=1,结果;
MPI_Bcast(&data,1,MPI_INT,0,bigworld);
数据*=(1+排名);
MPI_减少(数据和结果),1,M