【问题标题】:Stuck at counting array elements greater than x in an array in C/MPI卡在计算 C/MPI 中数组中大于 x 的数组元素
【发布时间】:2020-10-31 01:01:33
【问题描述】:

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

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

#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,在这种情况下应该返回 2。

【问题讨论】:

  • 对于初学者来说,tam 被使用未初始化 [来自-Wall],所以portion 不会完全符合您的预期(即随机)。
  • 感谢@CraigEstey 的提醒,我在代码的那部分搞砸了,但仍然没有按预期工作=/。这个-Wall 有什么作用?
  • 与您上一个问题中的错误相同。计数是元素,而不是字节。
  • -Wall 是您提供给编译器以启用 all [嗯,大多数] 警告的选项。添加-Wextra [更多警告] 和-Werror [告诉编译器将警告视为错误]。您应该始终使用这些。他们可以在运行时节省 的调试。

标签: c mpi


【解决方案1】:

这是由我的顶级评论开始的。 tam 正在统一化

还有许多其他问题...

您正在执行 scanf 以获取 comp 的值,但在它下面的循环中,您正在分配一个新值(即提示的值被丢弃)。如果将原始值视为默认值 [如果 循环无法分配新值],那可能会非常好,但对我来说似乎有点摇摆不定。

AFAICT,您正尝试在 所有 进程中循环 workvet。但是,对于客户端来说,这什么都没有,因为你没有发回结果[见下文]。

客户端发回maior,但他们从不计算它的值。而且, main 接收该值。它自己计算一个。

maior 在您发布的代码中没有定义。并且,因此是 unitialized [甚至在 main 中]。

看起来您想要客户端发回单个标量他们的计算值maior,但他们确实这样做了没有计算它。

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

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

您没有提供任何示例数据,因此很难在此处进一步调试。部分问题在于,subvets 中只有 一些 值是基于 if [或者看起来...] 用 main 中的 scanf 初始化的。 p>

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

如果subvets 的设置循环在要发送的值方面是正确的(也就是说,只应发送某些选定的值),我不确定你是否可以使用二维数组方法做你想做的事有。

如果没有描述输入数据以及您想用它做什么的问题陈述,很难预测正确的代码是什么,但是...

一些猜测...

您在所有进程中计算highest [可能在主进程中无用],但随后 没有人 对它做任何事情。我的猜测是你想在client 进程中计算这个only。并且,将其作为maior 发送回主服务器。

那么,main 可以对所有客户端的maior 值求和吗?


更新:

我实际上将maior 更改为highest 以在此处发布问题,所以它会有点道理(葡萄牙语中的maior 更大)但在所有情况下都没有这样做

正如我所提到的,我猜的差不多了——不用担心。 旁注:事实上,你的英语还不错。而且,你翻译代码真是太好了。其他一些人用英语发布,但将代码保留在他们的母语中。这可能会使事情变慢一点。有时,我将代码放入谷歌翻译中只是为了理解它。

我刚刚更新了没有翻译的代码以反映我的工作。所以,对于 subvets 部分,我实际上认为这是一个矩阵,我会将它的每一行作为一个数组发送到每个工作线程,并且 if 语句只读取到数组的大小已达到,因此,将其余值保留为 0(因为我使用了 calloc,因此使这种方法适合我必须解决的问题)

真的不需要二维数组。只需填充一个一维数组,然后将不同的偏移量和计数分配给该单个数组[见下文]。

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

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

还有一句很好的格言……不要复制代码

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

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

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

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

不管怎样,下面是代码。它已经过大量重构并且有很多 cmets:

#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;
}

【讨论】:

  • 我实际上将maior 更改为highest 以在此处发布问题,所以这会有点道理(葡萄牙语中的maior 更大)但在所有情况下都没有这样做,我'我很抱歉。我也在编辑我的帖子以添加问题描述。
  • 我刚刚更新了没有翻译的代码以反映我的工作。所以,对于subvets 部分,我实际上认为这是一个矩阵,我会将它的每一行作为一个数组发送到每个工作线程,并且if 语句在那里只读取直到已达到数组的大小,因此将其余值保留为 0(因为我使用了calloc,因此使这种方法适合我必须解决的问题)。
  • 非常感谢您的帮助。我改变了我的代码,考虑到你写的很多东西,以及我在阅读你的代码时的想法(顺便说一句写得非常好),现在它按预期工作了。
猜你喜欢
  • 2019-06-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-18
相关资源
最近更新 更多