MPI多进程统计文本字母数异常:进程数增加后计数错误
MPI多进程统计字母次数错误的修复方案
问题根源分析
你的代码在进程数大于2时计数错误,核心问题集中在以下几点:
- 计数数组未初始化:
letterCounts数组默认是内存垃圾值,累加后直接导致结果混乱。 - rank0分块发送逻辑错误:循环内每次将
start重置为0,所有非0进程收到的都是文件开头片段,而非各自的分块内容。 - rank0未参与统计:rank0仅负责分发文件,自身未统计任何字符,其
letterCounts的垃圾值会混入最终归并结果。 - 分块边界计算混乱:最后一块的长度逻辑错误,无法正确分配剩余内容。
- 资源未释放:文件未关闭、动态分配的缓冲区未释放,存在内存泄漏。
修正后的代码
// FILE READING USING MPI FUNCTION #include <stdio.h> #include <mpi.h> #include <stdlib.h> #include <string.h> int main(int argc, char* argv[]) { int size; int rank; MPI_Status status; int chunksize; MPI_Offset file_size; int letterCounts[26] = {0}; // 初始化计数数组为0 MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &size); MPI_Comm_rank(MPI_COMM_WORLD, &rank); if(size >= 101){ printf("Process failed: 进程数不能超过100\n"); MPI_Abort(MPI_COMM_WORLD, 1); } MPI_File file; int open_err = MPI_File_open(MPI_COMM_WORLD, "warandpeace.txt", MPI_MODE_RDONLY, MPI_INFO_NULL, &file); if (open_err != MPI_SUCCESS) { printf("进程%d: 打开文件失败!\n", rank); MPI_Abort(MPI_COMM_WORLD, 1); } // 获取文件大小 MPI_File_get_size(file, &file_size); // 计算每个进程的分片边界,处理无法整除的情况 chunksize = file_size / size; int remainder = file_size % size; MPI_Offset start = rank * chunksize + (rank < remainder ? rank : remainder); MPI_Offset end = start + chunksize + (rank < remainder ? 1 : 0); int local_size = end - start; // 分配本地缓冲区 char* buffer = (char*)malloc(local_size * sizeof(char)); if (!buffer) { printf("进程%d: 内存分配失败\n", rank); MPI_Abort(MPI_COMM_WORLD, 1); } // 每个进程直接读取自己负责的文件片段 MPI_File_read_at(file, start, buffer, local_size, MPI_CHAR, &status); // 统计本地字符 for (int j = 0; j < local_size; j++) { char c = buffer[j]; if (c >= 'a' && c <= 'z') { letterCounts[c - 'a']++; } else if (c >= 'A' && c <= 'Z') { letterCounts[c - 'A']++; } } // 归并所有进程的计数结果 int totalCounts[26]; MPI_Reduce(letterCounts, totalCounts, 26, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); // 输出最终统计结果 if (rank == 0) { for (int i = 0; i < 26; i++) { printf("%c: %d\n", 'a' + i, totalCounts[i]); } } // 释放资源 free(buffer); MPI_File_close(&file); MPI_Finalize(); return 0; }
关键修正说明
- 初始化计数数组:定义
letterCounts时直接初始化为{0},确保所有进程的计数从0开始。 - 改用
MPI_File_read_at分片读取:避免rank0单独读完整文件再分发的低效模式,每个进程直接读取自己负责的文件区间,彻底解决分发逻辑错误。 - 正确计算分片边界:处理文件大小无法被进程数整除的情况,前
remainder个进程多处理1个字符,保证所有内容都被覆盖。 - 所有进程参与统计:rank0不再仅做分发,而是和其他进程一起负责部分内容的统计,避免垃圾值进入归并操作。
- 资源释放:添加
free(buffer)和MPI_File_close(&file),修复内存泄漏和文件未关闭的问题。 - 增强错误处理:对文件打开、内存分配添加明确的错误提示,便于调试。
内容的提问来源于stack exchange,提问作者Suminda Sampath
相关产品推荐
相关产品推荐

