2011-10-22 44 views
1

我一直在我的代码中存在一段时间的错误,但无法弄清楚如何解决它。我试图实现的很容易:每个工人节点(即排名!= 0的节点)在涉及一些计算的正方形结构中获得一行(由一维arry表示)。计算完成后,该行将被发送回主设备。在简单的MPI发送/接收程序中的不清楚的行为

出于测试目的,不涉及计算。所有这一切发生的事情是:

  • 主机发送行号工人,工人使用的行数,结果值回

来计算相应的值

  • 工人发阵列现在,我的问题是这样的:

    • 如在一排(大小= 1006)和工人> 1
    • 的预期数达到一定大小的元素数所有作品10
    • 如果一行中的元素超过1006,工作人员无法关闭,程序也不会终止
    • 只有当我尝试将数组发送回主服务器时才会发生这种情况。如果我只是发回的INT,那么一切都OK(见doMasterTasks()和doWorkerTasks()注释掉行)根据过去的子弹点

    ,我认为必须有一些竞争条件,其只有当要发送回主站的数组达到一定大小时才会出现表面。

    你有什么想法可能是什么问题?

    编译下面代码:mpicc -O2 -std = C99 -o简单

    运行可执行像这样:的mpirun -np 3简单<大小>(例如1006或1007)

    这里的代码:

    #include "mpi.h" 
    #include <stdio.h> 
    #include <string.h> 
    #include <stdlib.h> 
    
    #define MASTER_RANK 0 
    #define TAG_RESULT 1 
    #define TAG_ROW 2 
    #define TAG_FINISHOFF 3 
    
    int mpi_call_result, my_rank, dimension, np; 
    
    // forward declarations 
    void doInitWork(int argc, char **argv); 
    void doMasterTasks(int argc, char **argv); 
    void doWorkerTasks(void); 
    void finalize(); 
    void quit(const char *msg, int mpi_call_result); 
    
    void shutdownWorkers() { 
        printf("All work has been done, shutting down clients now.\n"); 
        for (int i = 0; i < np; i++) { 
         MPI_Send(0, 0, MPI_INT, i, TAG_FINISHOFF, MPI_COMM_WORLD); 
        } 
    } 
    
    void doMasterTasks(int argc, char **argv) { 
        printf("Starting to distribute work...\n"); 
        int size = dimension; 
        int * dataBuffer = (int *) malloc(sizeof(int) * size); 
    
        int currentRow = 0; 
        int receivedRow = -1; 
        int rowsLeft = dimension; 
        MPI_Status status; 
    
        for (int i = 1; i < np; i++) { 
         MPI_Send(&currentRow, 1, MPI_INT, i, TAG_ROW, MPI_COMM_WORLD); 
         rowsLeft--; 
         currentRow++; 
    
        } 
    
        for (;;) { 
    //  MPI_Recv(dataBuffer, size, MPI_INT, MPI_ANY_SOURCE, TAG_RESULT, MPI_COMM_WORLD, &status); 
         MPI_Recv(&receivedRow, 1, MPI_INT, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status); 
    
         if (rowsLeft == 0) 
          break; 
    
         if (currentRow > 1004) 
          printf("Sending row %d to worker %d\n", currentRow, status.MPI_SOURCE); 
         MPI_Send(&currentRow, 1, MPI_INT, status.MPI_SOURCE, TAG_ROW, MPI_COMM_WORLD); 
         rowsLeft--; 
         currentRow++; 
        } 
        shutdownWorkers(); 
        free(dataBuffer); 
    } 
    
    void doWorkerTasks() { 
        printf("Worker %d started\n", my_rank); 
    
        // send the processed row back as the first element in the colours array. 
        int size = dimension; 
        int * data = (int *) malloc(sizeof(int) * size); 
        memset(data, 0, sizeof(size)); 
    
        int processingRow = -1; 
        MPI_Status status; 
    
        for (;;) { 
    
         MPI_Recv(&processingRow, 1, MPI_INT, 0, MPI_ANY_TAG, MPI_COMM_WORLD, &status); 
         if (status.MPI_TAG == TAG_FINISHOFF) { 
          printf("Finish-OFF tag received!\n"); 
          break; 
         } else { 
    //   MPI_Send(data, size, MPI_INT, 0, TAG_RESULT, MPI_COMM_WORLD); 
          MPI_Send(&processingRow, 1, MPI_INT, 0, TAG_RESULT, MPI_COMM_WORLD); 
         } 
        } 
    
        printf("Slave %d finished work\n", my_rank); 
        free(data); 
    } 
    
    int main(int argc, char **argv) { 
    
    
        if (argc == 2) { 
         sscanf(argv[1], "%d", &dimension); 
        } else { 
         dimension = 1000; 
        } 
    
        doInitWork(argc, argv); 
    
        if (my_rank == MASTER_RANK) { 
         doMasterTasks(argc, argv); 
        } else { 
         doWorkerTasks(); 
        } 
        finalize(); 
    } 
    
    void quit(const char *msg, int mpi_call_result) { 
        printf("\n%s\n", msg); 
        MPI_Abort(MPI_COMM_WORLD, mpi_call_result); 
        exit(mpi_call_result); 
    } 
    
    void finalize() { 
        mpi_call_result = MPI_Finalize(); 
        if (mpi_call_result != 0) { 
         quit("Finalizing the MPI system failed, aborting now...", mpi_call_result); 
        } 
    } 
    
    void doInitWork(int argc, char **argv) { 
        mpi_call_result = MPI_Init(&argc, &argv); 
        if (mpi_call_result != 0) { 
         quit("Error while initializing the system. Aborting now...\n", mpi_call_result); 
        } 
        MPI_Comm_size(MPI_COMM_WORLD, &np); 
        MPI_Comm_rank(MPI_COMM_WORLD, &my_rank); 
    } 
    

    任何帮助非常感谢!

    最佳, 克里斯

  • +0

    有很多发送/接听电话,但没有错误检查..也许你应该检查第一 – stijn

    +0

    只好在此之前,没有造成洞察力。为了清楚起见,将其留出。但会再次检查,也许我忽略了一些东西。 – Christof

    回答

    5

    如果你看看你的doWorkerTasks,你看他们,因为他们收到发送一样多的数据信息; (并且他们再收到一个关闭它们)。

    但是您的主码:

    for (int i = 1; i < np; i++) { 
        MPI_Send(&currentRow, 1, MPI_INT, i, TAG_ROW, MPI_COMM_WORLD); 
        rowsLeft--; 
        currentRow++; 
    
    } 
    
    for (;;) { 
        MPI_Recv(dataBuffer, size, MPI_INT, MPI_ANY_SOURCE, TAG_RESULT, MPI_COMM_WORLD, &status); 
    
        if (rowsLeft == 0) 
         break; 
    
        MPI_Send(&currentRow, 1, MPI_INT, status.MPI_SOURCE, TAG_ROW, MPI_COMM_WORLD); 
        rowsLeft--; 
        currentRow++; 
    } 
    

    发送比接收NP-2更多的数据消息。特别是,它只会继续接收数据,直到它不再发送,即使应该有np-2更多未完成数据消息。将代码更改为以下内容:

    int rowsLeftToSend= dimension; 
    int rowsLeftToReceive = dimension; 
    
    for (int i = 1; i < np; i++) { 
        MPI_Send(&currentRow, 1, MPI_INT, i, TAG_ROW, MPI_COMM_WORLD); 
        rowsLeftToSend--; 
        currentRow++; 
    
    } 
    
    while (rowsLeftToReceive > 0) { 
        MPI_Recv(dataBuffer, size, MPI_INT, MPI_ANY_SOURCE, TAG_RESULT, MPI_COMM_WORLD, &status); 
        rowsLeftToReceive--; 
    
        if (rowsLeftToSend> 0) { 
         if (currentRow > 1004) 
          printf("Sending row %d to worker %d\n", currentRow, status.MPI_SOURCE); 
         MPI_Send(&currentRow, 1, MPI_INT, status.MPI_SOURCE, TAG_ROW, MPI_COMM_WORLD); 
         rowsLeftToSend--; 
         currentRow++; 
        } 
    } 
    

    现在有效。

    为什么代码不会死锁(注意这是死锁,不是竞赛条件;这是分布式计算的一种较为常见的并行错误)对于较小的消息大小是如何大多数MPI实现工作的一个微妙细节。通常,MPI实现只是将小消息“推”到管道上,而不管接收器是否准备好,但更大的消息(因为它们在接收端需要更多的存储资源)需要发送者和接收者之间的一些握手。 (如果您想了解更多信息,请搜索eager vs rendezvous协议)。

    所以对于小消息的情况下(小于1006个整数在这种情况下,和1个INT肯定工程,太)工人节点做他们发送的主人是否被接收他们。如果主设备,则调用MPI_Recv(),则消息已经存在,并且会立即返回。但事实并非如此,所以主方有待处理的消息;但没关系。主人发出了杀人消息,每个人都退出了。

    但对于较大的邮件,其余发送()■要有接收器particpating清除,并且因为接收器从来不会,其余工人挂起。

    请注意,即使对于没有死锁的小消息,代码也不能正常工作 - 缺少计算数据。

    更新:有你shutdownWorkers一个类似的问题:

    void shutdownWorkers() { 
        printf("All work has been done, shutting down clients now.\n"); 
        for (int i = 0; i < np; i++) { 
         MPI_Send(0, 0, MPI_INT, i, TAG_FINISHOFF, MPI_COMM_WORLD); 
        } 
    } 
    

    这里要发送到的所有过程,包括排名 0,一个做发送。原则上,MPI_Send应该死锁,因为它是一个阻塞发送,并且没有已经发布的匹配接收。你可以发布一个非阻塞接收以避免这种情况,但这是不必要的 - 等级0不需要让自己知道结束。因此,只要改变回路

    for (int i = 1; i < np; i++) 
    

    TL;博士 - 僵持你的代码,因为主并没有收到来自工人足够的信息;由于大多数MPI库的通用实现细节,它恰巧适用于小型邮件。

    +0

    哇,很好的回答,非常感谢! 我想出了一个部分工作的解决方案,但正如你所解释的,在一台机器上,它死锁并中止,而不是在第二台机器上。但是,两种MPI实现方式是不同的:一种似乎检查死锁状况,另一种不(或不关心)。将考虑您的建议并相应地更改代码。再次感谢! – Christof

    +0

    该代码可在我的本地计算机和群集上运行。但是,只要我们在IBM BlueGene/P机器上运行它,它就会失败。错误如下: “MPID_Send(65):DEADLOCK:尝试发送消息到本地进程而没有先前的匹配接收”。非常明确,我一直在使用Google,并试图改变代码。但是,我没有得到一个工作解决方案。您是否明白为什么BlueGene的MPI实现对此代码不满意? – Christof

    +0

    您的代码中还有一个错误,我没有发现。错误信息非常清楚 - 你试图发送到本地进程(例如,到你自己的等级),这对于阻塞发送会导致死锁。但由于上述原因,在这种情况下不会发生(消息足够小)。无论如何,MPICH2都会捕获这种情况。该错误在shutdownWorkers()中 - 您应该从1循环到np,而不是从0循环到np。 –