You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

关键修正说明

  1. 初始化计数数组:定义letterCounts时直接初始化为{0},确保所有进程的计数从0开始。
  2. 改用MPI_File_read_at分片读取:避免rank0单独读完整文件再分发的低效模式,每个进程直接读取自己负责的文件区间,彻底解决分发逻辑错误。
  3. 正确计算分片边界:处理文件大小无法被进程数整除的情况,前remainder个进程多处理1个字符,保证所有内容都被覆盖。
  4. 所有进程参与统计:rank0不再仅做分发,而是和其他进程一起负责部分内容的统计,避免垃圾值进入归并操作。
  5. 资源释放:添加free(buffer)和MPI_File_close(&file),修复内存泄漏和文件未关闭的问题。
  6. 增强错误处理:对文件打开、内存分配添加明确的错误提示,便于调试。

内容的提问来源于stack exchange,提问作者Suminda Sampath

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 19:55:16