C 来自未知源的MPI异步广播

C 来自未知源的MPI异步广播,c,asynchronous,mpi,communication,broadcast,C,Asynchronous,Mpi,Communication,Broadcast,我有一个C项目,有n个处理器在进行一种树搜索。在程序的任何给定时间,这些进程中的任何一个都可能找到感兴趣的内容,并希望将其异步发送给所有其他处理器 如何在其他进程上侦听新消息,而不必在每次循环迭代中遍历所有可能的发送者 我已经读过关于这个的其他问题,例如这个(),然而,到目前为止,我所看到的不是处理不可预测的发送者,就是循环遍历每个可能的发送者,这我并不喜欢 编辑以澄清: 将找到的值发送到根列组并从那里进行分发是不可能的。如果我没有这种情况,祖兰的回答会起作用,所以对其他人来说这可能会有所帮助。

我有一个C项目,有n个处理器在进行一种树搜索。在程序的任何给定时间,这些进程中的任何一个都可能找到感兴趣的内容,并希望将其异步发送给所有其他处理器

如何在其他进程上侦听新消息,而不必在每次循环迭代中遍历所有可能的发送者

我已经读过关于这个的其他问题,例如这个(),然而,到目前为止,我所看到的不是处理不可预测的发送者,就是循环遍历每个可能的发送者,这我并不喜欢

编辑以澄清:

将找到的值发送到根列组并从那里进行分发是不可能的。如果我没有这种情况,祖兰的回答会起作用,所以对其他人来说这可能会有所帮助。
在我的例子中,不同的列组可以(而且肯定会)找到他们需要多次共享的内容(这意味着竞争条件也可能发生)。

您可以选择一个根列组,并将异步发送与源自该根的异步广播相结合。下面是一个小例子:

#include <stdio.h>
#include <mpi.h>

int main()
{
    int size, rank;
    MPI_Init(NULL, NULL);
    MPI_Comm_size(MPI_COMM_WORLD, &size);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);

    const int root = 0;
    const int found_tag = 42;
    const long long chunk = 100000ll;
    const long long target = 134861523ll;

    MPI_Request found_request;
    long long found_item;
    if (rank == root) {
        MPI_Irecv(&found_item, 1, MPI_LONG_LONG, MPI_ANY_SOURCE, found_tag, MPI_COMM_WORLD, &found_request);
    } else {
        MPI_Ibcast(&found_item, 1, MPI_LONG_LONG, root, MPI_COMM_WORLD, &found_request);
    }

    for (long long my_number = rank;; my_number += size) {
        if (my_number == target) {
            found_item = my_number;
            printf("I found it: %d, %lld\n", rank, found_item);
            // We don't stop here, we will continue until the root tells us to stop
            // This also works if we are the root
            MPI_Send(&my_number, 1, MPI_LONG_LONG, root, found_tag, MPI_COMM_WORLD);
        }
        // Avoid checking MPI_Test too often.
        if ((1 + (my_number / size)) % chunk == 0) {
            if (rank == root) {
                int found = 0;
                MPI_Test(&found_request, &found, MPI_STATUS_IGNORE);
                if (found) {
                    printf("Someone told me that he found it: %d, %lld\n", rank, found_item);
                    MPI_Request bcast_request;
                    MPI_Ibcast(&found_item, 1, MPI_LONG_LONG, root, MPI_COMM_WORLD, &bcast_request);
                    MPI_Wait(&bcast_request, MPI_STATUS_IGNORE);
                    break;
                }
            } else {
                int found = 0;
                MPI_Test(&found_request, &found, MPI_STATUS_IGNORE);
                if (found) {
                    break;
                }
            }
        }
    }
    printf("%d, %lld\n", rank, found_item);
    MPI_Finalize();
}
#包括
#包括
int main()
{
整数大小、等级;
MPI_Init(NULL,NULL);
MPI_通信大小(MPI_通信世界和大小);
MPI通信等级(MPI通信世界和等级);
const int root=0;
const int found_tag=42;
const long chunk=100000ll;
常量长目标=134861523ll;
找到MPI\u请求\u请求;
长期发现的项目;
if(秩==根){
MPI\u Irecv(找到项、1、MPI\u长、MPI\u任意源、找到标记、MPI\u通信世界和找到请求);
}否则{
MPI\u Ibcast(&found\u item,1,MPI\u LONG\u LONG,root,MPI\u COMM\u WORLD,&found\u请求);
}
for(long long my_number=rank;my_number+=size){
如果(我的号码==目标){
找到的项目=我的项目编号;
printf(“我找到它:%d,%lld\n”,排名,找到\u项);
//我们不会在这里停止,我们会继续,直到根告诉我们停止
//如果我们是根,这同样有效
MPI_发送(&M_编号,1,MPI_LONG,根,找到标记,MPI_COMM_WORLD);
}
//避免过于频繁地检查MPI_测试。
如果((1+(我的号码/大小))%chunk==0){
if(秩==根){
int=0;
MPI\u测试(&found\u请求,&found,MPI\u状态\u忽略);
如果(找到){
printf(“有人告诉我他找到了它:%d,%lld\n”,排名,找到了\u项);
MPI_请求bcast_请求;
MPI\u Ibcast(&found\u item,1,MPI\u LONG\u LONG,root,MPI\u COMM\u WORLD,&bcast\u请求);
MPI\u等待(&B广播请求,MPI\u状态\u忽略);
打破
}
}否则{
int=0;
MPI\u测试(&found\u请求,&found,MPI\u状态\u忽略);
如果(找到){
打破
}
}
}
}
printf(“%d,%lld\n”,排名,找到项);
MPI_Finalize();
}
这段代码假设您只找到一个数字-一次。查找多个数字需要一些附加代码


正如您所指出的,您不能发布未知发件人的广播。现在,您可以发布一个
MPI\u Allreduce
(甚至
MPI\u Allgather
)-之后所有列组都将知道找到的值。但是,不能只发布一次异步操作,因为发布后无法更改值。

首先,与其他集合原语一样,MPI广播操作需要所有进程参与,这意味着如果要使用集合操作,则需要所有进程输入语句。 这是因为MPI广播原语在同一个集合语句中执行发送和接收操作:

这种抽象通常允许非幼稚的集体实现,所有进程实际上都可以为广播做出贡献。这里有很好的解释:

如果您希望异步地收到有关找到的每个值的通知,因为它可能以某种方式对您的算法有所贡献,那么使用异步发送在每个进程上循环可能是您的最佳选择,尤其是当这些值很小时


在您的问题中,您似乎只关心使用循环来侦听消息。请注意,您可以使用
MPI\u ANY\u SOURCE
作为探测和接收消息的源参数来避免接收循环。这避免了循环,但只探测/接收一条消息,因此根据您要执行的操作,您可能希望循环,直到队列中不再有消息,或者使用更复杂的方法来防止大量消息阻止进程的进行。

您可以在周期性出现的同步点发送值。您可以延迟找到的号码的传递并在本地收集号码,直到下一个同步点出现,而不是立即发送找到的号码。在这一点上,你一次发送一组数字。当您一次发送一组数据时,创建同步点可能需要一些开销

可以使用all创建同步点。每个进程将其本地收集的号码发送给所有其他进程。allgather就像一个屏障,在它之后,您可以进行实际的广播。在该障碍之后,所有进程都知道广播的大小以及它们包含的项目数

Allgather也只是发送数字,为什么不直接发送您的数字呢?因为allgather操作可能相对“空”(如果你知道我的意思的话…),其他进程不知道它发生时的时间点。如果选择固定的同步点,每个人都知道何时进行同步,并且可能有多个数字需要传输

另一个选择可能是研究军事革命(