C++如何处理大文件读写?(内存映射与分块策略)

2次阅读

内存映射(mmap/mapviewoffile)比fread快2–5倍,但需满足随机访问、少修改、容忍缺页延迟;小文件或未优化io设置时反而更慢;须注意页对齐、跨平台释放差异及返回值校验。

C++如何处理大文件读写?(内存映射与分块策略)

内存映射比 fread 快,但不是所有场景都适用

直接用 mmaplinux/macos)或 CreateFileMappingwindows)读大文件,本质是让内核把文件页“挂”进进程虚拟地址空间,避免用户态缓冲区拷贝。但前提是文件可随机访问、不频繁修改、且你愿意承担页面错误带来的延迟抖动。

  • 只读场景下,mmap + 指针偏移访问通常比循环 fread 快 2–5 倍;写入时若用 MAP_SHARED,脏页回写由内核异步完成,但崩溃可能丢数据
  • 小文件(mmap 启动开销反而更高;fread 配合 64KB–1MB 缓冲区更稳
  • Windows 上 CreateFileMapping 要求先用 CreateFile 打开文件,且 dwMaximumSizeHigh/Low 必须精确——传 0 会失败,得用 GetFileSize 先查大小

分块读写必须自己控制缓冲区边界

fread/fwrite 处理 GB 级文件时,系统默认 libc 缓冲(如 8KB)完全不够看,频繁 syscall 是性能杀手。关键不是“要不要分块”,而是“块大小和对齐怎么定”。

  • 推荐块大小设为 1MB(1024 * 1024),既避开大多数文件系统块大小(4KB/64KB),又适配 CPU cache line 和 TLB 容量
  • 读取时务必检查 fread 返回值:它可能少于请求字节数(EOF 或磁盘错误),不能假设“调用一次就读满”
  • 写入前用 fseek 定位时,若文件未提前 ftruncate 到目标长度,空洞区域在 Linux 上会填 0,在 Windows 上可能报错 Invalid argument

std::ifstream 在大文件上默认很慢,必须关同步

c++ 标准库流默认与 C stdio 同步(std::ios_base::sync_with_stdio(true)),每次 operator>> 都要锁 stdin/stdout,GB 文件可能慢 10 倍以上。

  • 开头加一句 std::ios_base::sync_with_stdio(false),再禁用 cin.tie(nullptr),能逼近 fread 性能
  • std::ifstream::read 仍走 libc 缓冲,别用 getline 或格式化输入(>>)解析二进制数据——它会逐字节扫描换行符,O(n) 变成 O(n²)
  • 若需按行处理文本大文件,改用 fgets + 自己管理缓冲区,或用 mmapstrchr 扫描,别信 std::getline 的“简洁”

跨平台 mmap 封装要注意页对齐和错误码

Linux 的 mmap 要求 offset 是页大小(getpagesize())整数倍;Windows 的 MapViewOfFile 要求 offset 是 64KB 对齐。硬写死 4096 会崩在 Windows 上。

立即学习C++免费学习笔记(深入)”;

  • Linux 下用 sysconf(_SC_PAGESIZE) 获取真实页大小;Windows 下用 GetSystemInfo(&si); si.dwAllocationGranularity(通常是 64KB)
  • mmap 失败返回 MAP_FAILED(即 (void*)-1),不是 nullptr;Windows 下 MapViewOfFile 失败才返回 nullptr
  • 释放时,Linux 用 munmap,Windows 用 UnmapViewOfFile + CloseHandle(两次调用),漏掉任意一个都会泄漏句柄或内存

真正卡住人的从来不是“选 mmap 还是 fread”,而是 mmap 的 offset 对齐、Windows 句柄生命周期、以及 fread 返回值校验——这些点错一个,程序就静默出错或内存暴涨。

text=ZqhQzanResources