使用MPI实现矩阵乘法

3
我正在尝试运行一个MPI矩阵乘法示例,但我修改了它以读取文件,结果出现问题。
具体来说,我遇到了这个错误:
Entering first MPI_Recv in p0 and recieving data from slave processor 1
Fatal error in MPI_Recv: Invalid count, error stack:
MPI_Recv(186): MPI_Recv(buf=0xbfd30930, count=-1807265191, MPI_FLOAT, src=0, tag=1, MPI_COMM_WORLD, status=0x804b080) failed
MPI_Recv(104): Negative count, value is -1807265191

这是修改后的代码:

 MPI_Init(&argc, &argv);  
 MPI_Comm_rank(MPI_COMM_WORLD, &id);  
 MPI_Comm_size(MPI_COMM_WORLD, &p);  
 slaves = p-1;  //slaves=numworkers
 /*---------------------------- master ----------------------------*/  
 if(id == 0) 
   {  
  /* check the number of arguments */

    if(argc!=4)
    {
        printf("Invalid number of aguements!\n./program matrix_file1 matrix_file2 result_matrix_file\n");
        return -1;
    }

         /* read matrix A */
    printf("read matrix A from %s\n", argv[1]);
    read_matrix( argv[1],&a, &sa, &i, &j);

    if(i != j) 
    { 
        printf("ERROR: matrix A not square\n"); 
        return -1;
    }

        

    n = i;



  /* read matrix B */
     printf("read matrix B from %s\n", argv[2]);
     read_matrix(argv[2],&b, &sb, &i, &j);

     

    if(i != j) 
    {     
          printf("ERROR: matrix B not square\n"); 
          return -1; 
    }   

    if(n != i) 
    {   printf("ERROR: matrix A and B incompatible\n"); 
        return -1; 
    }



    if((n%p)!=0)
    {
        printf("ERROR: %d processor(s) cannot divide matrices %d x %d! \n", p,n,n); 
        return -1;
    }
    
    
 
        rows = n/slaves;
        offset=0;
        remainPart=n%slaves;


    for(dest=1;dest<=slaves;dest++)
    {
        

        if(remainPart>0)
        {
            originalRows=rows;
            ++rows;
            remainPart--;
             printf("Sending %d rows to task %d offset=%d\n",rows,dest,offset);
            MPI_Send(&offset, 1, MPI_INT, dest, 1, MPI_COMM_WORLD);  
            MPI_Send(&rows, 1, MPI_INT, dest, 1, MPI_COMM_WORLD);  
            MPI_Send(&a[offset][0], rows*n, MPI_FLOAT,dest,1, MPI_COMM_WORLD);  
            MPI_Send(&b, n*n, MPI_FLOAT, dest, 1, MPI_COMM_WORLD);  
            offset = offset + rows;   
            rows = originalRows;  

        }
        else
        {
             printf("Sending %d rows to task %d offset=%d\n",rows,dest,offset);
            MPI_Send(&offset, 1, MPI_INT, dest, 1, MPI_COMM_WORLD);  
            MPI_Send(&rows, 1, MPI_INT, dest, 1, MPI_COMM_WORLD);  
            MPI_Send(&a[offset][0], rows*n, MPI_FLOAT,dest,1, MPI_COMM_WORLD);  
            MPI_Send(&b, n*n, MPI_FLOAT, dest, 1, MPI_COMM_WORLD);  
            offset = offset + rows; 
        }
    }
    /* initialize matrix C */

    sc = (float*)malloc(n*n*sizeof(float));
    memset(sc, 0, n*n*sizeof(float));
    c = (float**)malloc(n*sizeof(float*));
    for(i=0; i<n; i++) c[i] = &sc[i*n];

    /* wait for results from all worker tasks */  
   for (k=1; k<=slaves; k++)      
   {              
    source = k;  
    printf("Entering first MPI_Recv in p0 and recieving data from slave processor %d\n", source);
    MPI_Recv(&offset, 1, MPI_INT, source, 2, MPI_COMM_WORLD, &status); 
    printf("Entering second MPI_Recv in p0\n"); 
    MPI_Recv(&rows, 1, MPI_INT, source, 2, MPI_COMM_WORLD, &status);
    printf("Entering third MPI_Recv in p0\n");  
    MPI_Recv(&c[offset][0], rows*n, MPI_FLOAT, source, 2, MPI_COMM_WORLD, &status);  
   }     
    

   write_matrix(argv[3], sc, i, j);

    free(sc);
    free(c);
  }   



if(id>0)
{
      source = 0; 
        //printf("Entered first MPI_Recv for process %d\n", id); 
       MPI_Recv(&offset, 1, MPI_INT, source, 1, MPI_COMM_WORLD, &status);  
        //printf("Entered second MPI_Recv for process %d\n", id);
       MPI_Recv(&rows, 1, MPI_INT, source, 1, MPI_COMM_WORLD, &status);  
        //printf("Entered third MPI_Recv for process %d\n", id);
       MPI_Recv(&a, rows*n, MPI_FLOAT, source, 1, MPI_COMM_WORLD, &status);
        //printf("Entered fourth MPI_Recv for process %d\n", id);  
       MPI_Recv(&b, n*n, MPI_FLOAT, source, 1, MPI_COMM_WORLD, &status);  
       /* Matrix multiplication */  
       for (k=0; k<n; k++)  
        for (l=0; l<rows; l++) {   
         for (m=0; m<n; m++)  
          c[l][k] = c[l][k] + a[l][m] * b[m][k];  
        }  


        //printf("Entered first MPI_send for process %d\n", id);
       MPI_Send(&offset, 1, MPI_INT, 0, 2, MPI_COMM_WORLD);  
        //printf("Entered second MPI_send for process %d\n", id);
       MPI_Send(&rows, 1, MPI_INT, 0, 2, MPI_COMM_WORLD);  
        //printf("Entered third MPI_send for process %d\n", id);
       MPI_Send(&c, rows*n, MPI_FLOAT, 0, 2, MPI_COMM_WORLD);  

        
}






MPI_Finalize();}

之前,我错误地遍历了所有的进程,而不只是工作线程,所以我已经修复了这个问题,但我完全不知道随机负数从哪里来。特别是在打印语句之后出现了什么情况。
printf("Entering first MPI_Recv in p0 and recieving data from slave processor %d\n", source);
    MPI_Recv(&offset, 1, MPI_INT, source, 2, MPI_COMM_WORLD, &status); 
    printf("Entering second MPI_Recv in p0\n"); 
    MPI_Recv(&rows, 1, MPI_INT, source, 2, MPI_COMM_WORLD, &status);
    printf("Entering third MPI_Recv in p0\n");  
    MPI_Recv(&c[offset][0], rows*n, MPI_FLOAT, source, 2, MPI_COMM_WORLD, &status);  

这只是由1和原始维度n乘以分配给从节点的行的平均值组成的。

更新:好的,问题的一部分似乎是我的数组在主进程中已经分配了空间,但从进程没有。

意识到这一点后,我添加了矩阵的缓冲区,在检查处理器是否为工作进程之前传输它们的数据。尽管显然打印语句不会显示出来,所以它并不完全按计划工作。

float buffA[n][n];
float buffB[n][n];
float buffC[n][n];

for(l=0;l<n;l++)
    for(m=0;m<n;m++)
    {
        buffA[l][m]=a[l][m];
        buffB[l][m]=b[l][m];

                        //buffA[l][m]=sa[(i*n) + j];
                        //buffB[l][m]=sb[(i*n) + j];
        printf("buffA[%d][%d] =%f\n",l,m, buffA[l][m]);
        printf("buffB[%d][%d] =%f\n",l,m,buffB[l][m]);
    }

if(id>0)
{
        /*read_matrix( argv[1],&a, &sa, &i, &j);
        read_matrix(argv[2],&b, &sb, &i, &j);*/



        source = 0; 
        printf("Entered first MPI_Recv for process %d\n", id); 
       MPI_Recv(&offset, 1, MPI_INT, source, 1, MPI_COMM_WORLD, &status);  
        printf ("offset =%d\n", offset);
       MPI_Recv(&rows, 1, MPI_INT, source, 1, MPI_COMM_WORLD, &status);  
        printf ("row =%d\n", rows);
       MPI_Recv(&buffA[offset][0], rows*n, MPI_FLOAT, source, 1, MPI_COMM_WORLD, &status);
        printf("buffA[offset][0] =%f\n", buffA[offset][0]); //they're not getting the matrices 
       MPI_Recv(&buffB, n*n, MPI_FLOAT, source, 1, MPI_COMM_WORLD, &status);  
        //printf ("b=\n");

       /* Matrix multiplication */  
       for (k=0; k<n; k++)  
        for (l=0; l<rows; l++) {   
            //c[l][k]=0.0;
         for (m=0; m<n; m++)  
          buffC[l][k] = buffC[l][k] + buffA[l][m] * buffB[m][k];  
            //printf("c[%d][%d]= %f\n", l,k, c[l][k]);
        }  


        //printf("Entered first MPI_send for process %d\n", id);
       MPI_Send(&offset, 1, MPI_INT, source, 2, MPI_COMM_WORLD);  
        //printf("Entered second MPI_send for process %d\n", id);
       MPI_Send(&rows, 1, MPI_INT, source, 2, MPI_COMM_WORLD);  
        //printf("Entered third MPI_send for process %d\n", id);
       MPI_Send(&buffC, rows*n, MPI_FLOAT, source, 2, MPI_COMM_WORLD);  

        printf("Exit via MPI_send for process %d\n", id);
}

错误编号也已更改,但我不确定这是否意味着什么。
Fatal error in MPI_Recv: Invalid count, error stack:
MPI_Recv(186): MPI_Recv(buf=0xbf8e642c, count=-8, MPI_FLOAT, src=0, tag=1,MPI_COMM_WORLD, status=0x804c088) failed
MPI_Recv(104): Negative count, value is -8

好的,现在我发现维度n没有被传输,这导致了最初的随机负数。因此,我添加了一个send和recv来传输n。现在似乎最后一个问题是如何为MPI传输动态分配的数组。还在努力解决。

更新

它有效了,当前的工作代码是这样的,虽然乘法到处都是,但我想这是迈出的第一步。XP

if(id>0)
{

        


        source = 0; 
        printf("Entered first MPI_Recv for process %d\n", id); 
       MPI_Recv(&offset, 1, MPI_INT, source, 1, MPI_COMM_WORLD, &status);  
        printf ("offset =%d\n", offset);
       MPI_Recv(&rows, 1, MPI_INT, source, 1, MPI_COMM_WORLD, &status); 
         MPI_Recv(&n, 1, MPI_INT, source, 1, MPI_COMM_WORLD, &status); 
        printf ("row =%d\nn=%d\n", rows,n);

        float buffA[rows][n];
        float buffB[n][n];
        float buffC[rows][n];

        
       MPI_Recv(&buffA[offset][0], rows*n, MPI_FLOAT, source, 1, MPI_COMM_WORLD, &status);
        printf("buffA[offset][0] =%f\n", buffA[offset][0]); //they're not getting the matrices 
       MPI_Recv(&buffB, n*n, MPI_FLOAT, source, 1, MPI_COMM_WORLD, &status);  
        //printf ("b=\n");

       /* Matrix multiplication */  
       for (k=0; k<n; k++)  
        for (l=0; l<rows; l++) {   
            //c[l][k]=0.0;
         for (m=0; m<n; m++)  
          //buffC[l][k] = buffC[l][k] + buffA[l][m] * buffB[m][k];  
            //printf("c[%d][%d]= %f\n", l,k, c[l][k]);
            buffC[l][k] = buffC[l][k] + buffA[l][m] * buffB[m][k];  

        }  


        //printf("Entered first MPI_send for process %d\n", id);
       MPI_Send(&offset, 1, MPI_INT, source, 2, MPI_COMM_WORLD);  
        //printf("Entered second MPI_send for process %d\n", id);
       MPI_Send(&rows, 1, MPI_INT, source, 2, MPI_COMM_WORLD);  
        //printf("Entered third MPI_send for process %d\n", id);
       MPI_Send(&buffC, rows*n, MPI_FLOAT, source, 2, MPI_COMM_WORLD);  

        printf("Exit via MPI_send for process %d\n", id);
}

结果

0.00 -0.00 -0.00 -0.00 -0.00 -0.00 0.00 0.00 
0.00 -0.00 -0.00 -0.00 -1.26 -1.26 -0.00 -1.26 
-0.00 -1.26 -0.00 0.00 -0.00 0.00 0.00 0.00 
-0.00 0.00 -0.00 -0.00 0.00 -0.00 -0.00 0.00 
0.00 0.00 0.00 0.00 -0.00 -1.26 -0.00 0.00 
-0.00 -0.00 0.00 35833769696167556769392596671120015360.00 0.00 0.00 -0.00 0.00 
-0.00 -0.00 0.00 -0.00 -0.00 0.00 0.00 0.00 
0.00 -nan -0.00 -0.00 -0.00 -0.00 -0.00 -0.00 

你尝试过通过调试器运行它吗?代码并不是很长。你可以在调用MPI_Recv时中断,相对容易地找出问题所在。 - Wesley Bland
到目前为止,我只使用了打印语句。有什么建议吗?普通的gdb似乎有点复杂。 - user2243369
好的,值得一试。 - user2243369
对于MPI调试,我也喜欢运行多个gdb实例。通常我会执行类似于mpirun -np N xterm -e gdb ./mpiprog的命令,这样我就可以并行调试所有线程,观察发送和接收等操作。在这个例子中,N应该很小,这样你就不必处理大量窗口。 - Nigel
1个回答

2

在分布式环境中,打印语句是极不可靠的。它们之间并没有保证按照顺序到达。GDB其实并不那么糟糕。你不需要附加到所有进程,只需选择一个即可。你可以查看我的答案(stackoverflow.com/questions/17347778/…)来了解如何操作。


但这不是问题的答案。如果OP自己发布一个答案而不是将答案编辑到问题本身中,那么对所有人来说都会更好。问题没有必要有一个答案,伪装成答案的评论仍然是评论。 - High Performance Mark
很好。实际上,这个问题可能应该被关闭,因为调试问题就是所谓的“太局限”。然而,我在 Meta 上寻求了一些建议,并发现(http://meta.stackexchange.com/questions/125384/debug-this-code-for-me-questions)这里接受的回答是告诉人们如何调试。如果这个问题被关闭了,我也不会反对。我只是不知道 OP 会说什么才是真正的“答案”对于这个“问题”。 - Wesley Bland

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接