createfilemapping 不一定提升性能,因其依赖页错误触发i/o,随机访问小数据且工作集超内存时反而更慢;仅在多次读取只读大文件、大小≤70%物理内存时受益。

为什么用 CreateFileMapping 不一定提升性能
直接映射大文件进内存,不等于自动变快。Windows 的 CreateFileMapping 本质是建立虚拟地址空间与磁盘文件的关联,真正读写时仍依赖页面错误(page fault)触发实际 I/O。如果频繁随机访问小块数据、且工作集远超物理内存,反而会加剧缺页中断和内存抖动,比顺序 ReadFile 更慢。
它真正受益的场景很具体:需要多次反复读取同一份只读大文件的任意位置(比如资源包、数据库快照、图像图集),且总大小在可用物理内存 70% 以内。
- 映射后首次访问某页才加载,不是打开就全读入——这点常被误认为“预加载”
-
FILE_ATTRIBUTE_NO_BUFFERING和映射不能共存;想绕过系统缓存,得用FILE_FLAG_NO_BUFFERING+ 直接 I/O,但要求对齐、缓冲区页对齐,复杂度陡增 - 32 位进程地址空间紧张,映射多个大文件容易撞到 2GB 用户态上限,64 位下才真正释放潜力
CreateFileMapping 最简安全调用要点
跳过所有花哨参数,只保留最稳的只读映射路径:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 先用
CreateFile打开文件,dwDesiredAccess = GENERIC_READ,dwFlagsAndAttributes = FILE_ATTRIBUTE_READONLY(非必须但防误写) -
CreateFileMapping的flProtect = PAGE_READONLY,dwMaximumSizeHigh/dwMaximumSizeLow必须填文件真实大小(用GetFileSizeEx获取,别信GetFileSize的高位截断) -
MapViewOfFile传FILE_MAP_READ,别用FILE_MAP_ALL_ACCESS——哪怕你开了PAGE_READWRITE映射,只读访问也够用,且避免触发写时复制(Copy-on-Write)开销 - 映射失败时,检查
GetLastError():常见ERROR_NOT_ENOUGH_MEMORY(地址空间碎片或太大)、ERROR_INVALID_HANDLE(CreateFile返回了INVALID_HANDLE_VALUE却没检查)
映射后怎么安全读数据?避开指针越界和生命周期陷阱
拿到 MapViewOfFile 返回的指针,不是拿了就能随便用。关键约束有两条:一是范围,二是释放时机。
- 指针有效范围严格等于你调用
MapViewOfFile时传的dwNumberOfBytesToMap——哪怕文件更大,也不能靠指针算术越过这个长度,否则触发访问违规(STATUS_ACCESS_VIOLATION) - 映射视图(view)和映射对象(mapping object)是分离的:
CloseHandle关闭映射句柄不影响已映射的内存;但进程退出前必须调用UnmapViewOfFile,否则该段虚拟地址无法被复用,长期运行服务会因地址空间耗尽崩溃 - 多线程读同一映射区域无需加锁(只读),但若映射时用了
PAGE_READWRITE且多线程写,则必须自己同步——系统不保证写操作原子性 - 示例片段:
HANDLE hFile = CreateFile(L"data.bin", GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, nullptr); HANDLE hMap = CreateFileMapping(hFile, nullptr, PAGE_READONLY, 0, 0, nullptr); LARGE_INTEGER size; GetFileSizeEx(hFile, &size); void* pView = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, (SIZE_T)size.QuadPart); // ... 使用 pView 指向的内存 UnmapViewOfFile(pView); CloseHandle(hMap); CloseHandle(hFile);
比 CreateFileMapping 更轻量的替代方案:什么时候该放弃它
如果你只是想加速单次顺序读、或者文件小于 10MB、或者目标是跨平台(Linux/macOS 用 mmap),那映射反而添乱。
- 顺序读大文件:用
SetFilePointerEx+ReadFile配合 64KB~1MB 缓冲区,再启用FILE_FLAG_SEQUENTIAL_SCAN,让系统预读优化更有效 - 需要部分修改文件:映射 + 写入不如
SetFilePointerEx+WriteFile精确控制偏移,还省去处理写时复制和脏页刷盘逻辑 - C++23 的
std::span+ 自定义 allocator 能模拟类似效果,但无内核级映射优势;真正跨平台需求建议封装mmap/CreateFileMapping抽象层,而非强绑 Windows API - 调试时发现
VirtualQuery查到映射区域状态为MEM_IMAGE或MEM_MAPPED,但访问极慢——大概率是硬盘响应延迟高或文件碎片严重,映射解决不了物理 I/O 瓶颈
真正难的从来不是调通 API,而是判断“此刻是否真需要它”。地址空间、页面调度、磁盘寻道——这些底层机制不会因为一行 MapViewOfFile 就自动对你友好。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










