mpi(c语言)程序为什么出现[cb81ea2c449b:01915] Read -1, expected 9000, errno = 1


#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include"mpi.h"

int isShui(int k)
{
    if(k == 0||k == 1) return 0;
    char num_str[4];
    sprintf(num_str, "%d", k);
    int digits = strlen(num_str);
    int total = 0;
    for (int i = 0; i < digits; i++) {
        int digit = num_str[i] - '0';
        total += digit * digit * digit* digit;
    }
    return k == total;
}

int main(int argc,char **argv)
{
    int rank,size;
    int i,j,cnt;
    int n;
    int *num;
    int a = 1000,b = 10000;
    n = b-a;
    MPI_Init(&argc,&argv);
    MPI_Comm_size(MPI_COMM_WORLD,&size);
    MPI_Comm_rank(MPI_COMM_WORLD,&rank);
    if(rank==0)
    {
//        printf("Please input n:\n");
//        scanf("%d",&n);
        num = (int*)malloc(sizeof(int)*n);
        //printf("Please input %d data:\n",n);
        int i;
        for(i=0,j=a;i<n;i++,j++)
            num[i] = j;
            //scanf("%d",&num[i]);
    }
    MPI_Bcast(&n,1,MPI_INT,0,MPI_COMM_WORLD);
//scatterv 
//    //********************************************************************
//     // 计算每个进程负责处理的数据量
    int* recvcounts = (int*) malloc(size * sizeof(int));
    int* displs = (int*) malloc(size * sizeof(int));
    int cs = n / size;
    int r = n % size;
    for (int i = 0; i < size; i++) {
        recvcounts[i] = cs;
        if (i < r) {
            recvcounts[i] += 1;  
        }
        displs[i] = (i > 0 ? displs[i-1] + recvcounts[i-1] : 0);
    }

    // 将数据 scatterv 分发到每个进程
    int* recvbuf = (int*) malloc(recvcounts[rank] * sizeof(int));
    MPI_Barrier(MPI_COMM_WORLD);
    MPI_Scatterv(num, recvcounts, displs, MPI_INT,
                 recvbuf, recvcounts[rank], MPI_INT, 0, MPI_COMM_WORLD);
    //***********************************************************


   int *suarr = (int*)malloc(sizeof(int)*recvcounts[rank]);
    for(i = 0,j = 0;i<recvcounts[rank];i++)
    {
        if(isShui(recvbuf[i]))//判断素数 
        {
            suarr[j] = recvbuf[i];
            j++;
        }
    }
    MPI_Reduce(&j,&cnt,1,MPI_INT,MPI_SUM,0,MPI_COMM_WORLD);//总共cnt个素数 
    
    
    //Gatherv
    //********************************************************************************** 
    MPI_Gather(&j,1,MPI_INT,recvcounts,1,MPI_INT,0,MPI_COMM_WORLD);//接收数量的数组 
    recvcounts[rank] = j; 
    for(i = 0;i<size;i++)
    displs[i] = (i>0?displs[i-1]+recvcounts[i-1]:0);
    int *ans = (int*)malloc(sizeof(int)*cnt);
    MPI_Gatherv(suarr,j,MPI_INT,ans,recvcounts,displs,MPI_INT,0,MPI_COMM_WORLD);
    //***********************************************************************************
    if(rank == 0)
    {
        for(i = 0;i<cnt;i++)
        {
            printf("%d ",ans[i]);
        }
        printf("\n");
    }
    MPI_Finalize();
    return 0;
}

img


虽然结果可以打印出来,但是为什么会出现红色框里面的内容,改怎么解决。
该程序的原题目为:
若一个四位数,它的每个位上的数字的4次幂之和等于它本身。例如:1634=14+63+33+44。请编写一个并行程序,把1000-9999之间的数分配给各进程,找出其中的水仙花数并打印出来。在你的主目录下,保存的文件名为rose.c。

这个问题发生的可能原因是由于程序试图读取一个无效的内存地址或者读取数据的长度超出了这个内存区域的范围,这往往会导致运行时出现异常和错误。

在你的代码中,可能与以下这一行相关:

MPI_Gatherv(suarr,j,MPI_INT,ans,recvcounts,displs,MPI_INT,0,MPI_COMM_WORLD);

你在调用 MPI_Gatherv 函数时,第二个参数为 j,表示需要发送的数据的长度。因此,这个参数要确保在发送时数据长度至少为 0。在你的代码中,你并没有对这个长度进行正确的初始化操作,可能导致在 MPI_Gatherv 函数中读取了不合法的数据。这个问题可能在你某个进程中发生,当这个进程的数据长度小于 j 时会尝试读取无效的数据进而导致错误。

为了解决这个问题,你可以对第二个参数进行正确的初始化操作。在你的代码中,j 记录的是当前进程中水仙花数的个数。

你可以在所有进程中,即在调用 isShui 函数之前,统计出 num 中水仙花数需要的内存大小,并将这个值广播到所有进程。因此,你可以在调用 MPI_Gatherv 函数之前,统计出所有进程中水仙花数的个数(即 cnt),并将这个值存储在一个数组中。这个数组的大小应为进程的个数(MPI_Comm_size 返回的值)。

具体的修改方式如下,修改后的变量名称我们使用了 recvcounts

int* recvcounts = (int*) malloc(size * sizeof(int));
int* displs = (int*) malloc(size * sizeof(int));
int cs = n / size;
int r = n % size;
for (int i = 0; i < size; i++) {
    recvcounts[i] = cs;
    if (i < r) {
        recvcounts[i] += 1;  
    }
    displs[i] = (i > 0 ? displs[i-1] + recvcounts[i-1] : 0);
}

// 统计所有进程中水仙花数的个数
MPI_Reduce(&j,&cnt,1,MPI_INT,MPI_SUM,0,MPI_COMM_WORLD);

int* recvbuf = (int*) malloc(recvcounts[rank] * sizeof(int));
MPI_Barrier(MPI_COMM_WORLD);
MPI_Scatterv(num, recvcounts, displs, MPI_INT,
             recvbuf, recvcounts[rank], MPI_INT, 0, MPI_COMM_WORLD);

int *suarr = (int*)malloc(sizeof(int)*recvcounts[rank]);
for(i = 0,j = 0;i<recvcounts[rank];i++) {
    if(isShui(recvbuf[i])) {
        suarr[j] = recvbuf[i];
        j++;
    }
}

// 将统计的所有进程中水仙花数的个数存储在 recvcounts 中
MPI_Gather(&j,1,MPI_INT,recvcounts,1,MPI_INT,0,MPI_COMM_WORLD);

MPI_Gatherv 函数中的参数修改如下:

MPI_Gatherv(suarr, j, MPI_INT, ans, recvcounts, displs, MPI_INT, 0, MPI_COMM_WORLD);

这样就能正确地进行 MPI_Gatherv 了。同时,由于在函数调用之前的缺失引起的错误是随机的(它取决于进程和数组大小等变量的排列方式),因此你可能需要尝试多次运行你的程序,来验证这个修改是否真正解决了问题。

不知道你这个问题是否已经解决, 如果还没有解决的话:
  • 以下回答来自chatgpt:

    对于问题提示[cb81ea2c449b:01915] Read -1, expected 9000, errno =1,这一般是由于读取文件时文件不存在或者文件格式不正确导致的错误,需要检查读取文件的路径和文件名是否正确,同时也可尝试使用其他方式读取文件或手动创建文件来解决。

    对于实现并行计算找出1000-9999之间的水仙花数的问题,以下是一个可能的解决方案:

    #include <stdio.h>
    #include <string.h>
    #include <mpi.h>
    
    int is_flower(int num) {
        int i, sum = 0;
        char str[5];
        sprintf(str, "%d", num); // 数字转字符串
        for (i = 0; i < 4; i++) {
            int digit = str[i] - '0'; // 取出每个数字
            sum += digit * digit * digit * digit; // 累加计算
        }
        return sum == num; // 判断是否为水仙花数
    }
    
    int main(int argc, char** argv) {
        int rank, size; // 接收mpi进程信息,rank表示进程号,size表示进程总数
        MPI_Init(&argc, &argv);
        MPI_Comm_rank(MPI_COMM_WORLD, &rank);
        MPI_Comm_size(MPI_COMM_WORLD, &size);
    
        int start = 1000 + rank; // 每个进程的起始数字
        int end = 10000; // 数字范围
        int step = size; // 步长
        int count = 0; // 计数器,累计所有进程找到的水仙花数
    
        int i;
        for (i = start; i < end; i += step) {
            if (is_flower(i)) {
                printf("rank %d: %d is a flower number.\n", rank, i);
                count++;
            }
        }
    
        int sum = 0; // 统计所有进程找到的水仙花数
        MPI_Reduce(&count, &sum, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD);
        if (rank == 0) {
            printf("Total: %d flower numbers.\n", sum);
        }
    
        MPI_Finalize();
        return 0;
    }
    

    该程序使用了MPI库实现并行计算,首先获取当前进程的进程号和进程总数,然后根据进程号分段计算,计算范围为1000-9999之间的数字,每个进程的步长为总进程数,计算出所有数字中的水仙花数,计数器记录水仙花数的个数,并使用MPI_Reduce()函数统计各个进程计算得到的水仙花数,最后由进程0号输出总共找到的水仙花数。


如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^