2012-05-08 31 views
3

我有一个我希望并行化的串行C++程序。我知道MPI的基础知识,MPI_Send,MPI_Recv等。基本上,我有一个数据生成算法,运行速度比数据处理算法快得多。目前它们是串联运行的,但我认为在根进程中运行数据生成,在从进程上完成数据处理,并从根发送消息给包含待处理数据的从站。这样,每个从属进程处理一个数据集,然后等待下一个数据集。当没有更多的工作时,MPI从属进程挂起

问题是,一旦根进程完成生成数据,程序就会挂起,因为从服务器正在等待更多。

这是问题的一个例子:

#include "mpi.h" 

#include <cassert> 
#include <cstdio> 

class Generator { 
    public: 
    Generator(int min, int max) : value(min - 1), max(max) {} 
    bool NextValue() { 
     ++value; 
     return value < max; 
    } 
    int Value() { return value; } 
    private: 
    int value, max; 

    Generator() {} 
    Generator(const Generator &other) {} 
    Generator &operator=(const Generator &other) { return *this; } 
}; 

long fibonnaci(int n) { 
    assert(n > 0); 
    if (n == 1 || n == 2) return 1; 
    return fibonnaci(n-1) + fibonnaci(n-2); 
} 

int main(int argc, char **argv) { 
    MPI_Init(&argc, &argv); 

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

    if (rank == 0) { 
    Generator generator(1, 2 * num_procs); 
    int proc = 1; 
    while (generator.NextValue()) { 
     int value = generator.Value(); 
     MPI_Send(&value, 1, MPI_INT, proc, 73, MPI_COMM_WORLD); 
     printf("** Sent %d to process %d.\n", value, proc); 
     proc = proc % (num_procs - 1) + 1; 
    } 
    } else { 
    while (true) { 
     int value; 
     MPI_Status status; 
     MPI_Recv(&value, 1, MPI_INT, 0, 73, MPI_COMM_WORLD, &status); 
     printf("** Received %d from process %d.\n", value, status.MPI_SOURCE); 
     printf("Process %d computed %d.\n", rank, fibonnaci(2 * (value + 10))); 
    } 
    } 

    MPI_Finalize(); 
    return 0; 
} 

显然以上并非一切都是“好习惯”,但它足以传达出点。

如果我从从进程中删除while(true),那么程序在每个从站退出时退出。我希望程序在根进程完成其工作并且所有从服务器已经处理完所有已发送的内容后才退出。

如果我知道有多少数据集可以生成,我可以运行这么多的进程,并且一切都会很好地退出,但这不是这种情况。

有什么建议吗? API中有什么可以做到这一点?用更好的拓扑可以更好地解决这个问题吗? MPI_IsendMPI_IRecv会更好吗?我对MPI相当陌生,所以对我很感兴趣。

感谢

+0

你的斐波那契实现是O(2^n)。你应该优化你的顺序算法。 – mfontanini

+0

我知道。这不是我正在解决的实际问题,这只是我能想到的最简单的例子,它模拟了问题。 –

+0

也许我错过了一些东西,但是在每个过程结束时不会有一个简单的障碍来解决您的问题? – suszterpatt

回答

5

通常的做法是要发送到的所有工作进程与信号他们退出的无限循环处理特殊标记的空消息。比方说,这个标签是42.你会做这样的事情在工人循环:发电循环后

while (true) { 
    int value; 
    MPI_Status status; 
    MPI_Recv(&value, 1, MPI_INT, 0, MPI_ANY_TAG, MPI_COMM_WORLD, &status); 
    if (status.MPI_TAG == 42) { 
    printf("Process %d exiting work loop.\n", rank); 
    break; 
    } 
    printf("** Received %d from process %d.\n", value, status.MPI_SOURCE); 
    printf("Process %d computed %d.\n", rank, fibonnaci(2 * (value + 10))); 
} 

的管理器进程会做这样的事情:

for (int i = 1; i < num_procs; i++) 
    MPI_Send(&i, 0, MPI_INT, i, 42, MPI_COMM_WORLD); 

关于你的下一个问题。在主进程中使用MPI_Isend()将反序列化执行并提高性能。但事实是,您发送的消息非常小,而且这些消息通常是内部缓冲的(警告 - 依赖于实现!),因此您的MPI_Send()实际上是非阻塞的,并且您已经有非串行执行。 MPI_Isend()返回一个MPI_Request句柄,您需要稍后处理。您可以等待它完成MPI_Wait()MPI_Waitall(),但您也可以直接拨打MPI_Request_free()并在操作结束时自动释放它。这通常是在你想异步发送很多消息并且不关心发送何时完成的情况下完成的,但是这是一个糟糕的做法,因为有大量未完成的请求会消耗大量宝贵的内存。至于工作进程 - 他们需要这些数据才能继续进行计算,因此使用MPI_Irecv()是没有必要的。

欢迎来到MPI编程的精彩世界!

+0

这正是我正在寻找的。我甚至没有想过以这种方式使用标签。很酷。就小消息而言,这只是一个简单的例子,我将发送的实际消息要大得多,所以我将不得不使用'MPI_Send'和'MPI_Isend'来查看哪个消息具有最佳性能。谢谢,我感谢帮助。 –

+1

只是一个侧面说明 - “MPI_Isend”不会比MPI_Send发送消息更快,但可以覆盖通信和计算,从而隐藏前者的延迟。 –

+0

谢谢你指出。我意识到这一点,但我没有足够的经验与MPI知道哪个更好。时间进行一些实验。感谢您的建议。 –

相关问题