gpt4 book ai didi

c++ - MPI 动态分配任务

转载 作者:行者123 更新时间:2023-11-28 04:14:34 25 4
gpt4 key购买 nike

我有一个在 Windows HPC 集群(12 个节点,每个节点 24 个内核)上运行的 C++ MPI 程序。

  • 程序的逻辑非常简单:
    1. 有任务池
    2. 开始时,程序将任务平均分配给每个 MPI 进程
    3. 每个 MPI 进程执行它们的任务
    4. 一切都完成后,使用 MPI reduce 将结果收集到根进程。

有一个问题。每个任务的执行时间都可能截然不同,我无法提前告知。平均分配任务将导致大量进程空闲等待。这会浪费大量计算机资源并使总执行时间变长。

我正在考虑一种可能有效的解决方案。

  • 过程是这样的。
    1. 任务池被分成小包(比如 10 个任务一个包)
    2. 每个MPI进程在空闲的时候(还没有收到包裹,或者上一个包裹收完了)每次取一个包裹
    3. 继续第2步,直到任务池耗尽
    4. 使用 MPI reduce 将所有结果收集到根进程

据我所知,这个方案需要一个跨节点/进程的通用计数器(以避免不同的 MPI 进程执行同一个包裹)并且改变它需要一些锁定/同步机制。它当然有其开销,但通过适当的调整,我认为它可以帮助提高性能。

我对 MPI 不是很熟悉并且有一些实现问题。我可以想到两种方法来实现这个通用计数器

  1. 利用MPI I/O技术,将这个计数器写入文件,当有包裹被取走时,增加这个计数器(当然需要文件锁机制)
  2. 使用 MPI 单侧通信/共享内存。将这个计数器放在共享内存中,并在取包裹时增加它。 (当然需要同步机制)

不幸的是,我对这两种技术都不熟悉,想探索上述两种方法的可能性、实现或可能的缺点。示例代码将不胜感激。

如果您有其他解决问题的方法或建议,那也很好。谢谢。

跟进:

感谢所有有用的建议。我按照使用进程 0 作为任务分发器的方案实现了一个测试程序。

#include <iostream>
#include <mpi.h>

using namespace std;

void doTask(int rank, int i){
cout<<rank<<" got task "<<i<<endl;
}

int main ()
{
int numTasks = 5000;
int parcelSize = 100;

int numParcels = (numTasks/parcelSize) + (numTasks%parcelSize==0?0:1);

//cout<<numParcels<<endl;

MPI_Init(NULL, NULL);

int rank, nproc;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &nproc);

MPI_Status status;
MPI_Request request;

int ready = 0;
int i = 0;
int maxParcelNow = 0;

if(rank == 0){
for(i = 0; i <numParcels; i++){
MPI_Recv(&ready, 1, MPI_INT, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status);
//cout<<i<<"Yes"<<endl;
MPI_Send(&i, 1, MPI_INT, status.MPI_SOURCE, 0, MPI_COMM_WORLD);
//cout<<i<<"No"<<endl;
}
maxParcelNow = i;
cout<<maxParcelNow<<" "<<numParcels<<endl;
}else{
int counter = 0;
while(true){
if(maxParcelNow == numParcels) {
cout<<"Yes exiting"<<endl;
break;
}
//if(maxParcelNow == numParcels - 1) break;
ready = 1;
MPI_Send(&ready, 1, MPI_INT, 0, 0, MPI_COMM_WORLD);
//cout<<rank<<"send"<<endl;
MPI_Recv(&i, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, &status);
//cout<<rank<<"recv"<<endl;
doTask(rank, i);
}
}

MPI_Bcast(&maxParcelNow, 1, MPI_INT, 0, MPI_COMM_WORLD);

MPI_Finalize();
return 0;
}

它不工作,它永远不会停止。关于如何使其工作的任何建议?这段代码是否反射(reflect)了正确的想法,还是我遗漏了什么?谢谢

最佳答案

[将我的评论转化为答案...]

给定 n 个进程,您可以让您的第一个进程 p0 为其他 n - 1 个进程分派(dispatch)任务。首先,它将与其他 n - 1 进程进行点对点通信,以便每个人都有工作要做,然后它将阻塞在 Recv 上。当任何给定进程完成时,比如 p3,它会将其结果发送回 p0。此时,p0 将向 p3 发送另一条消息,其中包含以下两种情况之一:

1) 另一个任务

2) 如果没有剩余任务,则发出某种终止信号。 (使用消息的“标签”是一种简单的方法。)

显然,p0 将循环执行该逻辑,直到没有剩余任务为止,在这种情况下,它也会调用 MPI_Finalize

与您在评论中的想法不同,这不是循环赛。它首先为每个进程或工作人员提供一份工作,然后在完成时返回另一份工作...

关于c++ - MPI 动态分配任务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56953723/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com