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

如何在C语言中高效读取大型实部/复数文本文件?

优化GB级实/复数文本文件的C语言读取性能

问题描述

现有代码通过fscanf逐元素读取实部或复数格式的大型文本文件,小文件场景下逻辑正常,但处理GB级文件时,因fscanf的频繁函数调用和通用格式解析开销,读取速度远低于二进制文件,需优化读取性能。

原代码

int io_read_array_real(char ordering, DTYPE *array,
                       int m, int n, FILE *ifile)
{
    int i, j, match;
    DTYPE elem;
    for (i = 0; i < m; i++)
    {
        for (j = 0; j < n; j++)
        {
            match = fscanf(ifile, "%e", &elem);
            if (match == 0)
            {
                printf("An error occurred while parsing the file!\n");
                return (-1);
            }
            if (ordering == 'C')
                *(array + RTC(i, j, m)) = elem;
            else if (ordering == 'R')
                *(array + i * n + j) = elem;
            else
                return (-1);
        }
    }
    return (0);
}

int io_read_array_complex(char ordering, CDTYPE *array,
                          int m, int n, FILE *ifile)
{
    int i, j, info;
    DTYPE zreal, zimag;
    for (i = 0; i < m; i++)
    {
        for (j = 0; j < n; j++)
        {
            info = fscanf(ifile, " (%e%ej) ", &zreal, &zimag);
            if (info != 2)
            {
                fprintf(stderr, "Input file in wrong format at (%d,%d) info = %d!\n"
                                "strerror: %s\n",
                        i, j, info, strerror(errno));
                return (-1);
            }
            if (ordering == 'C')
                *(array + RTC(i, j, m)) = zreal + I * zimag;
            else if (ordering == 'R')
                *(array + i * n + j) = zreal + I * zimag;
            else
                return (-1);
        }
    }
    return (0);
}

文件格式

实数格式:

1.233e-3 2.231e-1 ...
2.335e-4 8.241e-2 ...
...

复数格式:

(1.233e-3+3.239e-4j) (1.233e-3+3.239e-4j) ...
(7.684e-2+8.269e-5j) (1.233e-3+3.239e-4j) ...
...

优化方案

1. 增大文件缓冲区

fscanf默认缓冲区较小,频繁磁盘IO是核心瓶颈之一。通过setvbuf设置大尺寸全缓冲区,减少IO交互次数:

// 打开文件后立即设置,缓冲区建议设为1MB(可根据内存调整)
char *buffer = malloc(1024 * 1024);
if (!buffer) {
    perror("malloc failed");
    return -1;
}
setvbuf(ifile, buffer, _IOFBF, 1024 * 1024);
// 注意:文件关闭前不要释放缓冲区

2. 批量读取+手动解析

逐元素调用fscanf的函数调用和通用格式解析开销极大,改为一次性读取大块数据到内存,再针对固定格式手动解析,能大幅降低开销。

实数版本优化代码

#include <string.h>
#include <ctype.h>
#include <stdlib.h>

int io_read_array_real_fast(char ordering, DTYPE *array, int m, int n, FILE *ifile) {
    const size_t buf_size = 1024 * 1024;
    char *buffer = malloc(buf_size);
    if (!buffer) {
        perror("malloc failed");
        return -1;
    }
    setvbuf(ifile, buffer, _IOFBF, buf_size);

    size_t bytes_read;
    char *ptr, *endptr;
    int count = 0;
    const int total_elems = m * n;

    while ((bytes_read = fread(buffer, 1, buf_size, ifile)) > 0) {
        ptr = buffer;
        while (ptr < buffer + bytes_read && count < total_elems) {
            // 跳过空白字符
            while (ptr < buffer + bytes_read && isspace((unsigned char)*ptr)) ptr++;
            if (ptr >= buffer + bytes_read) break;

            // 解析浮点数
            DTYPE elem = strtod(ptr, &endptr);
            if (endptr == ptr) {
                fprintf(stderr, "Failed to parse real element at position %d\n", count);
                free(buffer);
                return -1;
            }
            ptr = endptr;

            // 写入数组
            if (ordering == 'C') {
                int i = count / n;
                int j = count % n;
                array[RTC(i, j, m)] = elem;
            } else if (ordering == 'R') {
                array[count] = elem;
            } else {
                free(buffer);
                return -1;
            }
            count++;
        }
    }

    free(buffer);
    if (count != total_elems) {
        fprintf(stderr, "Expected %d elements, only read %d\n", total_elems, count);
        return -1;
    }
    return 0;
}

复数版本优化代码

针对固定的(x+yj)格式,直接定位实部、虚部的位置解析:

#include <string.h>
#include <ctype.h>
#include <stdlib.h>

int io_read_array_complex_fast(char ordering, CDTYPE *array, int m, int n, FILE *ifile) {
    const size_t buf_size = 1024 * 1024;
    char *buffer = malloc(buf_size);
    if (!buffer) {
        perror("malloc failed");
        return -1;
    }
    setvbuf(ifile, buffer, _IOFBF, buf_size);

    size_t bytes_read;
    char *ptr, *endptr;
    int count = 0;
    const int total_elems = m * n;

    while ((bytes_read = fread(buffer, 1, buf_size, ifile)) > 0) {
        ptr = buffer;
        while (ptr < buffer + bytes_read && count < total_elems) {
            // 跳过空白和左括号
            while (ptr < buffer + bytes_read && (isspace((unsigned char)*ptr) || *ptr == '(')) ptr++;
            if (ptr >= buffer + bytes_read) break;

            // 解析实部
            DTYPE zreal = strtod(ptr, &endptr);
            if (endptr == ptr) {
                fprintf(stderr, "Failed to parse real part of element %d\n", count);
                free(buffer);
                return -1;
            }
            ptr = endptr;

            // 跳过正负号
            while (ptr < buffer + bytes_read && (*ptr == '+' || *ptr == '-')) ptr++;
            if (ptr >= buffer + bytes_read) break;

            // 解析虚部
            DTYPE zimag = strtod(ptr, &endptr);
            if (endptr == ptr || (endptr < buffer + bytes_read && *endptr != 'j')) {
                fprintf(stderr, "Failed to parse imaginary part of element %d\n", count);
                free(buffer);
                return -1;
            }
            ptr = endptr + 1; // 跳过'j'
            // 跳过右括号
            while (ptr < buffer + bytes_read && *ptr == ')') ptr++;

            // 写入数组
            CDTYPE val = zreal + I * zimag;
            if (ordering == 'C') {
                int i = count / n;
                int j = count % n;
                array[RTC(i, j, m)] = val;
            } else if (ordering == 'R') {
                array[count] = val;
            } else {
                free(buffer);
                return -1;
            }
            count++;
        }
    }

    free(buffer);
    if (count != total_elems) {
        fprintf(stderr, "Expected %d elements, only read %d\n", total_elems, count);
        return -1;
    }
    return 0;
}

3. 额外优化建议

  • 内存对齐:使用posix_memalign或aligned_alloc分配目标数组,确保内存对齐,提升CPU写入效率。
  • 并行解析:对于超大型文件,可将文件分块读取后用多线程并行解析,注意保证数据写入顺序正确。
  • 定制化解析:如果文件格式完全固定,可编写更精简的浮点数解析函数替代strtod,进一步降低解析开销(比如直接处理指数、小数点等固定格式)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 11:46:02