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

为何更大写缓冲区下缓冲I/O比直接I/O耗时更长?

问题描述

测试I/O性能时发现一个反常现象:

  • 程序1:设置4096字节的stdio流缓冲区,执行1亿次单字节写入,耗时40.806秒
struct timespec start, finish, delta;

char write_buffer[4096];
char buffer_to_write[1] = {[0 ... 0] = 0x00};

FILE* fd = fopen("file.txt", "wb");
setbuf(fd, write_buffer);

clock_gettime(CLOCK_REALTIME, &start);
for (int i = 0; i < 100000000; i++) {
   fwrite(buffer_to_write, sizeof(char), sizeof(buffer_to_write), fd);
}
clock_gettime(CLOCK_REALTIME, &finish);
  • 程序2:使用直接I/O,执行24414次4096字节缓冲区写入(数据量与程序1相近),仅耗时0.5秒
struct timespec start, finish, delta;

char buffer_to_write[4096] = {[0 ... 4095] = 0x00};

int fd = open("file.txt", O_WRONLY, 0);

clock_gettime(CLOCK_REALTIME, &start);
for (int i = 0; i < 24414; i++) {
   write(fd, buffer_to_write, sizeof(buffer_to_write));
}
clock_gettime(CLOCK_REALTIME, &finish);

原以为两者的系统调用量应该相同(每4096次单字节写触发一次内核写入),但无法理解为何缓冲I/O程序耗时远长。


原因分析

核心差异在于用户态的操作累积开销,而非系统调用次数。虽然两者最终触发的内核write系统调用次数相近,但用户态的执行成本天差地别:

1. 函数调用次数的数量级差距

程序1执行了1亿次fwrite调用,程序2仅执行24414次write调用。哪怕每次fwrite只比write多几纳秒的用户态逻辑(比如参数校验、缓冲区剩余空间计算),1亿次的累积开销也会达到数十秒级别,这是最主要的耗时来源。

2. 缓冲区的细碎管理成本

fwrite处理单字节写入时,每次都要执行:

  • 检查缓冲区剩余空间
  • 将单字节拷贝到缓冲区对应位置
  • 更新写入指针与剩余字节数
  • 判断是否需要触发内核写入

这些细碎操作在1亿次循环中反复执行,每一步的微小开销都会被无限放大。而直接调用write写入大块数据时,没有这些用户态的缓冲区管理步骤。

3. 编译器优化的差异

程序2的循环逻辑简单,编译器可以轻松进行循环展开、冗余操作消除等优化,大幅提升效率。但程序1的循环中调用了标准库函数fwrite,编译器无法深入优化其内部逻辑;同时单字节写入的模式也很难被合并为批量操作,优化空间极小。

4. 内存访问效率差异

单字节写入会频繁修改缓冲区的单个字节,导致CPU缓存行频繁被标记为脏,后续缓存同步操作带来额外开销。而大块连续内存的写入缓存命中率更高,内存读写效率远高于细碎的单字节操作。

验证建议

修改程序1,改用fwrite批量写入4096字节,执行24414次,此时两者的耗时会非常接近——因为用户态的函数调用次数、缓冲区管理成本完全对齐了。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 08:37:32