MPI 动态分配任务

MPI dynamically allocate tasks

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

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

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

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

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

  1. 使用MPI I/O技术,将这个计数器写在文件中,当一个包裹被拿走时,增加这个计数器(肯定需要文件锁定机制)
  2. 一侧使用 MPI communication/shared 内存。将这个计数器放在共享内存中,并在取包裹时增加它。 (肯定需要同步机制)

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

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

跟进:

感谢所有有用的建议。我按照使用进程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;
}

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

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

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

1) 另一个任务

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

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

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