卡在计算 C/MPI 中数组中大于 x 的数组元素

Stuck at counting array elements greater than x in an array in C/MPI

所以,我遇到了这个问题,我有一个数组,我必须在我的数组中找到大于索引数 k 的数字计数。所以我实施了一个主从策略,我有一个负责 I/O 的主人并将工作分配给工人。在主线程中,我创建了矩阵形状的数组,因此我可以轻松地将子数组传递给工作人员(我知道这听起来很奇怪)。然后也在主线程中,我从输入中读取所有值到我的子数组,并将 comp (比较值)设置为 k 索引值的值。

然后我将工作部分大小、比较值和工作数据传递给所有线程(包括获得其工作份额的主线程)。最后,每个 worker 完成自己的工作并向 master 报告其结果,master 在从 worker 接收数据的同时将它们的值与自己的值相加,然后将总结果打印在屏幕上。

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

int main(int argc, char *args[]){
    int rank, psize;
    MPI_Status status;
    MPI_Init(&argc, &args);
    MPI_Comm_size(MPI_COMM_WORLD, &psize);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    int *workvet, worksize, comp;
    
    if(rank == 0){

        int tam, k;
        int **subvets, portion;

        scanf("%d", &tam);
        scanf("%d", &k);
        
        portion = ceil((float)tam/(float)psize);

        subvets = malloc(sizeof(int) * psize);
        for(int i = 0; i < psize; i++)
            subvets[i] = calloc(portion, sizeof(int));
        for(int i = 0; i < psize; i++){
            for(int j = 0; j < portion; j++){
                if((i*j+j) < tam)
                    scanf("%d ", &subvets[i][j]);
                if((i*j+j) == k)
                    comp = subvets[i][j];
            }
        }
        
        for(int i = 1; i < psize; i++){
            MPI_Send(&portion, 1, MPI_INT, i, i, MPI_COMM_WORLD);
            MPI_Send(&comp, 1, MPI_INT, i, i, MPI_COMM_WORLD);
            MPI_Send(subvets[i], portion, MPI_INT, i, i, MPI_COMM_WORLD);
        }
        workvet = calloc(portion, sizeof(int));
        workvet = subvets[0];
        worksize = portion;
    } else {

        MPI_Recv(&worksize, 1, MPI_INT, 0, rank, MPI_COMM_WORLD, &status);
        MPI_Recv(&comp, 1, MPI_INT, 0, rank, MPI_COMM_WORLD, &status);
        workvet = calloc(worksize, sizeof(int));
        MPI_Recv(workvet, worksize, MPI_INT, 0, rank, MPI_COMM_WORLD, &status);
    }

    int maior = 0;
    for(int i = 0; i < worksize; i++){
        if(workvet[i] > comp)
            maior++;
    }
    
    if(rank == 0){
        int temp;
        for(int i = 1; i < psize; i++){
            MPI_Recv(&temp, 1, MPI_INT, i, rank, MPI_COMM_WORLD, &status);
            maior += temp;
        }
        printf("%d números maiores que %d", maior, comp);
    } else {
        MPI_Send(&maior, 1, MPI_INT, 0, rank, MPI_COMM_WORLD);
    }
    
    MPI_Finalize();
}

我的问题是它看起来像是陷入了循环,在尝试调试时我在主 for 中放置了一个 printf,它在子数组中进行比较并且确实无限打印,但是,当我在代码中的其他任何地方放置相同的打印时,它不会被打印。我不知道我哪里失败了,也不知道如何调试我的代码。

输入数据:

10 // size
7  // k
1 2 3 4 5 6 7 8 9 10 // elements

因此,我的程序应该计算有多少元素大于索引 7 的元素,对应于值 8,在这种情况下应该 return 2。

这是我的最高评论开头。 tam 正在 单元化

还有一些其他问题...

您正在执行 scanf 以获得 comp 的值,但在它下面的循环中,您正在 分配 一个新值给它(即提示值被丢弃)。如果将原始值视为默认值 [if 循环无法分配新值],那可能完全没问题,但对我来说似乎有点摇摇欲坠。

AFAICT,您正在尝试在 all 进程中循环 workvet。但是,对于客户端,这 没有任何作用,因为您不会发回结果 [见下文]。

客户端正在发回 maior,但他们从不计算它的值。而且, main not 接收到该值。它自己计算其中之一。

maior 在您的 posted 代码中没有定义。并且,因此 unitialized [即使在 main].

看起来你希望 客户端发回他们的的单个标量值] maior 的计算值,但他们对其进行 计算。

因此,客户端发回一个垃圾 maior 主进程尝试求和的值。

您正在向客户发送 portion,但他们收到的是 worksize。并且,在 main 发送之后,它将 portion 分配给 worksize。我建议在所有地方使用相同的名称以减少混淆。

您没有提供任何示例数据,因此很难在此处进一步调试。部分问题是 subvets 中只有 some 的值是用 main 中的 scanf 初始化的,基于 if [左右似乎...]。

因此,客户端将循环遍历给定 subvets 数组中可能未初始化的值 [发送给接收它的客户端 workvet]。

如果 subvets 的设置循环就发送哪些值而言是正确的(即,只应发送某些选定的值),我不确定您可以使用你有二维数组的方法。

如果没有描述输入数据和你想用它做什么的问题陈述,很难判断什么是正确的代码,但是...

一些猜测...

您正在计算所有进程中的 highest [可能在主进程中没有用],但是 没有人 对它做任何事情。我的猜测是您想在 client 进程 only 中计算它。并且,将其作为 maior.

发送回 main

然后,main 可以对所有客户端的 maior 个值求和吗?


更新:

I actually changed maior to highest to post the issue here, so it would make a bit of sense (maior is greater in portuguese) but failed to do so for all instances

正如我所提到的,我也猜到了——不用担心。 旁注:事实上,你的英语很好。而且,很高兴你翻译了代码。其他一些 post 是英文的,但代码保留为他们的母语。这可以减慢速度。有时,我将代码放入 Google translate 只是为了理解它。

I just updated the code without the translation to reflect what I'm working on. So, for the subvets part I actually thought of this being a matrix, where I would send each of its lines as being one array to each of the worker threads, and the if statement is there to only read up until the size of the array has been reached, thus, leaving the rest of the values as 0 (because I used calloc, thus making this approach fit to the problem I have to solve)

确实不需要二维数组。只需填充一个一维数组,然后为每个工作人员提供不同的偏移量并计数到该单个数组中[见下文]。

通过尝试在一个函数中完成所有事情 main,这可能是导致分离主任务和辅助任务的一些问题的原因。

通过将事情分解为 [更多] 功能,这可以使事情变得更容易。我们可以在 master 和 worker 中对相同的数据使用相同的变量名,而不会出现任何命名冲突。

此外,一个很好的格言...... 不要复制代码

各种 MPI_* 调用需要很多参数,因为它们是通用的。将它们隔离为包装函数可以使事情更简单,调试更容易。

请注意 MPI_Send/MPI_Recv 的第二个参数是 countnot 字节数(因此,not sizeof)(即错误)。通过将它们放在包装函数中,调用可以固定在一个地方一次

我确实对拆分逻辑做了一点改动。在您的代码 [AFAICT] 中,您让 main/master 进程进行了一些计算。这很好,但我更喜欢将主进程用作控制进程,而不是被大量数据计算所阻碍。所以,在我的版本中,只有工作进程实际处理数组。

有时它有助于将计算 algorithm/logic 与 MPI 代码隔离开来。我在下面通过将它放在一个函数 docalc 中来做到这一点。这允许在最后添加诊断交叉检查。

无论如何,下面是代码。它已经过大量重构并且有很多评论:

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

// _dbgprt -- debug print
#define _dbgprt(_fmt...) \
    do { \
        printf("%d: ",myrank); \
        printf(_fmt); \
    } while (0)

#ifdef DEBUG
#define dbgprt(_fmt...) \
    _dbgprt(_fmt)
#else
#define dbgprt(_fmt...) \
    do { \
    } while (0)
#endif

int myrank;                         // current rank
int numproc;                        // number of processes in comm group

// dataload -- read in the data
int *
dataload(FILE *xfsrc,int worksize)
{
    int *workvet;

    // get enough space
    workvet = calloc(worksize,sizeof(int));

    // fill the array
    for (int idx = 0;  idx < worksize;  ++idx)
        fscanf(xfsrc,"%d",&workvet[idx]);

    return workvet;
}

// docalc -- count number of values greater than limit
int
docalc(int *workvet,int worksize,int k)
{
    int count = 0;

    for (int idx = 0; idx < worksize; ++idx) {
        if (workvet[idx] > k)
            count += 1;
    }

    return count;
}

// sendint -- send some data
void
sendint(int rankto,int *data,int count)
{
    int tag = 0;

    // NOTE: second argument is an array _count_ and _not_ the number of bytes
    MPI_Send(data,count,MPI_INT,rankto,tag,MPI_COMM_WORLD);
}

// recvint -- receive some data
void
recvint(int rankfrom,int *data,int count)
{
    int tag = 0;
    MPI_Status status;

    MPI_Recv(data,count,MPI_INT,rankfrom,tag,MPI_COMM_WORLD,&status);
}

// worker -- perform all worker operations
void
worker(void)
{
    int master = 0;

    // get array count
    int worksize;
    recvint(master,&worksize,1);

    // get limit value
    int k;
    recvint(master,&k,1);

    // allocate space for data
    int *workvet = calloc(worksize,sizeof(int));

    // get that data
    recvint(master,workvet,worksize);

    // calculate number of elements higher than limit
    int count = docalc(workvet,worksize,k);

    // send back result
    sendint(master,&count,1);
}

// master -- perform all master operations
void
master(int argc,char **argv)
{
    int isfile;
    FILE *xfsrc;
    int workrank;

    // get the data either from stdin or from a file passed on the command line
    do {
        isfile = 0;
        xfsrc = stdin;

        if (argc <= 0)
            break;

        xfsrc = fopen(*argv,"r");
        if (xfsrc == NULL) {
            perror(*argv);
            exit(1);
        }

        isfile = 1;
    } while (0);

    // get number of data elements
    int worksize;
    fscanf(xfsrc,"%d",&worksize);

    // get limit [pivot]
    int k;
    fscanf(xfsrc,"%d",&k);

    dbgprt("master: PARAMS worksize=%d k=%d\n",worksize,k);

    // read in the data array
    int *workvet = dataload(xfsrc,worksize);
    if (isfile)
        fclose(xfsrc);

    // get number of workers
    // NOTE: we do _not_ have the master do calculations [for simplicity]
    // usually, for large data, we want the master free to control things
    int numworkers = numproc - 1;

    // get number of elements for each worker
    int workper = worksize / numworkers;

    dbgprt("master: LOOP numworkers=%d workper=%d\n",numworkers,workper);

    // send data to other workers
    int remain = worksize;
    int offset = 0;
    int portion;
    for (workrank = 1;  workrank < numproc;  ++workrank,
        offset += portion, remain -= portion) {
        // get amount for this worker
        portion = workper;

        // last proc must get all remaining work
        if (workrank == (numproc - 1))
            portion = remain;

        dbgprt("master: WORK/%d offset=%d portion=%d\n",
            workrank,offset,portion);

        // send the worker's data count
        sendint(workrank,&portion,1);

        // send the pivot point
        sendint(workrank,&k,1);

        // send the data to worker
        sendint(workrank,&workvet[offset],portion);
    }

    // accumulate count
    int total = 0;
    int count;
    for (workrank = 1;  workrank < numproc;  ++workrank) {
        recvint(workrank,&count,1);
        total += count;
    }

    printf("%d numbers bigger than %d\n",total,k);

    // do cross check of MPI result against a simple single process solution
#ifdef CHECK
    count = docalc(workvet,worksize,k);
    printf("master count was %d -- %s\n",
        count,(count == total) ? "PASS" : "FAIL");
#endif
}

// main -- main program
int
main(int argc,char **argv)
{

    MPI_Init(&argc,&argv);
    MPI_Comm_size(MPI_COMM_WORLD,&numproc);
    MPI_Comm_rank(MPI_COMM_WORLD,&myrank);

    // skip over program name
    --argc;
    ++argv;

    if (myrank == 0)
        master(argc,argv);
    else
        worker();

    MPI_Finalize();

    return 0;
}