使用MPI实现C语言向量平均值计算时结果异常问题求助
问题排查与修复方案
核心错误点
- 数据接收类型不匹配:非0进程接收分片数据时,发送端用的是
MPI_FLOAT类型,接收端错误使用了MPI_INT,导致浮点数被强制解析为整数,数据完全错乱。 - 求和变量类型错误:
res、finres被定义为int类型,但存储的是float类型的向量元素求和结果,浮点数的小数部分会被直接截断,甚至出现整数部分为0的情况,直接导致最终平均值为0。 - 变量未初始化问题:
- 进程0中
num仅在给其他进程发消息的循环内赋值,当MPI进程总数为1时,不会进入该循环,num是未初始化的垃圾值 k的初始赋值位置错误,第一次发送数据分片时起始偏移量不对
- 进程0中
- 非法内存访问:最后一行的打印语句所有进程都会执行,但
finres仅在进程0中赋值,非0进程访问的是未初始化的垃圾值,会导致输出异常。
修复代码示例
#include <stdio.h> #include "mpi.h" int main(int argc, char** argv) { int my_rank; int plithos_dierg, k, num; int source, target; int tag1 = 50, tag2 = 60, tag3 = 70; int plithos; float data[100], mesi_timh = 0, m, f1; float data_loc[100]; // 求和变量改为float类型 float res, finres; MPI_Status status; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &my_rank); MPI_Comm_size(MPI_COMM_WORLD, &plithos_dierg); if (my_rank == 0) { printf("请输入向量的元素个数 \n"); scanf("%d", &plithos); printf("请输入向量的元素 \n"); for (int i = 0; i < plithos; i++) { printf("请输入第%d个元素 \n", i); scanf("%f", &data[i]); } // 提前计算num和k的初始值,避免进程数为1时未初始化 num = plithos / plithos_dierg; k = num; for (target = 1; target < plithos_dierg; target++) { MPI_Send(&plithos, 1, MPI_INT, target, tag1, MPI_COMM_WORLD); } for (target = 1; target < plithos_dierg; target++) { MPI_Send(&data[k], num, MPI_FLOAT, target, tag2, MPI_COMM_WORLD); k += num; } for (k = 0; k < num; k++) data_loc[k] = data[k]; } else { MPI_Recv(&plithos, 1, MPI_INT, 0, tag1, MPI_COMM_WORLD, &status); num = plithos / plithos_dierg; // 接收类型改为MPI_FLOAT MPI_Recv(&data_loc[0], num, MPI_FLOAT, 0, tag2, MPI_COMM_WORLD, &status); } res = 0; for (k = 0; k < num; k++) { res = res + (data_loc[k]); printf("循环中累加值 %f \n", res); } if (my_rank != 0) { printf("进程%d发送前的累加值 %f \n", my_rank, res); // 发送类型改为MPI_FLOAT MPI_Send(&res, 1, MPI_FLOAT, 0, tag3, MPI_COMM_WORLD); } else { printf("\n 进程%d的累加结果: %f\n", my_rank, res); finres = res; for (source = 1; source < plithos_dierg; source++) { // 接收类型改为MPI_FLOAT MPI_Recv(&res, 1, MPI_FLOAT, source, tag3, MPI_COMM_WORLD, &status); printf("\n 进程%d的累加结果: %f\n", source, res); finres = finres + res; } printf("向量元素总数是 %d \n", plithos); printf("总和是 %f \n", finres); printf("\n\n\n 向量的平均值是: %f\n", finres / plithos); } // 仅进程0打印最终结果,避免非0进程访问未初始化的finres if (my_rank == 0) { printf("\n\n\n 向量的平均值是: %f\n", finres / plithos); } MPI_Finalize(); return 0; }
额外优化建议
如果向量长度不能被进程总数整除,现有拆分逻辑会丢失最后几个元素,可以给最后一个进程多分配剩余的元素,或者用MPI_Scatterv接口实现不等长数据分发。
内容的提问来源于stack exchange,提问作者Petros Boufidis
相关产品推荐
相关产品推荐

