std::ifstream/ofstream默认拷贝慢因缓冲区仅1–4kb,引发百万级系统调用;应设64kb–1mb缓冲、禁用同步、用copy_file_range(linux)或copyfileex(windows)实现零拷贝。

用 std::ifstream 和 std::ofstream 默认拷贝为什么慢?
因为默认是逐字节读写,缓冲区极小(通常 1–4 KB),频繁系统调用导致 I/O 瓶颈。尤其在机械盘或网络文件系统上,read()/write() 调用次数可能达百万级。
实操建议:
- 显式设置大缓冲区:用
std::vector<char></char>分配 64 KB~1 MB 内存,避免反复堆分配 - 禁用同步:在流构造后立即调用
in.sync_with_stdio(false)和out.sync_with_stdio(false)(注意:这会禁用 C 风格stdio混用) - 关闭流的自动刷新:
in.rdbuf()->pubsetbuf(nullptr, 0)不起作用,应改用底层fdopen+read/write,但更推荐直接用系统调用
Linux/macOS 下用 copy_file_range 零拷贝是真的快
它让内核在文件描述符间直接搬运数据,不经过用户态内存,避免两次 memcpy。前提是源和目标都在支持的文件系统上(ext4/xfs/btrfs,且非 NFS/CIFS)。
实操建议:
- 检查可用性:
#ifdef __linux__,macOS 用copyfile(copyfile(3))替代 - 必须用
O_RDONLY和O_WRONLY打开 fd,不能用O_APPEND;目标文件需已存在且可写,或提前truncate - 循环调用,每次传入最大长度(如
1 ),检查返回值:0 表示 EOF,-1 且 <code>errno == EXDEV表示跨设备,需回落到普通读写
ssize_t n;
while ((n = copy_file_range(src_fd, nullptr, dst_fd, nullptr, 1048576, 0)) > 0) {
// continue
}
if (n == -1 && errno == EXDEV) {
fallback_copy(src_fd, dst_fd);
}
Windows 上绕过 CRT,用 CreateFile + CopyFileEx 最省心
CopyFileEx 是 Windows 原生高性能拷贝 API,内部自动选择最佳策略(包括使用 FILE_FLAG_NO_BUFFERING 和对齐 I/O),还支持暂停、回调、硬链接优化。
实操建议:
- 源/目标路径必须是绝对路径,否则可能失败;用
\?前缀突破 MAX_PATH 限制(如\?C:igile.bin) - 不要自己实现循环读写——
CopyFileEx的性能通常优于手写缓冲区,尤其在 NTFS 压缩或加密卷上 - 若需进度回调,提供非空
LPPROGRESS_ROUTINE;设dwCopyFlags为COPY_FILE_NO_BUFFERING | COPY_FILE_ALLOW_DECRYPTED_DESTINATION可进一步提速
跨平台 fallback 必须处理好错误和边界
零拷贝失败时,手动读写仍要避免常见坑:未对齐的缓冲区、忽略 read 返回值小于请求长度、没处理 EINTR。
实操建议:
- 缓冲区大小取 4096 的整数倍(页对齐),Linux 下用
posix_memalign分配;Windows 用_aligned_malloc - 每次
read后检查返回值:n == 0是 EOF,n 且 <code>errno == EINTR需重试,其他错误直接退出 - 写入必须循环直到全部写完:
while (written - 最后调用
fsync(Linux/macOS)或FlushFileBuffers(Windows)确保落盘,否则断电可能丢数据
真正难的不是选哪个 API,而是判断何时该退回到安全但稍慢的路径,以及如何让 fallback 不比原生慢十倍——缓冲区大小、对齐、错误重试逻辑,每个细节都影响最终速度。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











