提问人:Marco 提问时间:11/2/2023 最后编辑:Marco 更新时间:11/2/2023 访问量:34
为什么缓冲 I/O 比写入缓冲区更大的直接 I/O 花费更长的时间?
Why does buffered i/o take longer than direct i/o with bigger write-buffer?
问:
我已经测试了 I/O 性能,并注意到一个我无法解释的有趣行为。
有一个程序首先将 stream-buffer 设置为 4096 字节,然后写入一个字节 100.000.000 次。在我的目标系统上,此操作需要 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);
另一个程序使用直接 I/O,并使用直接 I/O 写入 4096 字节的缓冲区 24.414 次。写入的数据大小大致相同。此操作仅需 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);
据我了解,系统调用的数量应该相同。我不明白使用缓冲 I/O 的程序出于什么原因花费了这么多时间,即使只有第 4096 次循环通过,数据也应该发送到内核空间......
答:
0赞
Petr Skocik
11/2/2023
#1
非系统调用函数调用仍有开销。将其乘以 lot,您的开销可能会超过单个系统调用的开销,该调用可能会花费相当多的成本,但随后处理整个缓冲区而无需在每个字节上调用函数。
fwrite 不仅仅是函数调用的开销。它必须锁,做范围检查,并可能调用 memcpy(期望不仅仅是 1 个字节,否则你为什么不调用 fputc 或 fputc_unlocked呢?
我得到的比率约为 17:1,系统调用版本每字节大约需要 1.2ns(顺便说一句,这比我机器上的无操作函数调用略小)。将 fwrite 替换为 fputc/fputc_unlocked 可将其提高到大约 4:1。 在每次写入时使用大小相等的缓冲区和 stdio 大小写,而不是一次写入字节,大约是 1:1(缓存内 memcpy 相当快)。
评论
0赞
Marco
11/2/2023
这是有道理的,但是当将相同大小的缓冲区与 stdio 一起使用时:一次使用缓冲 I/O 而不是常规 I/O 来处理大字节块(大于缓冲区大小)有什么优势吗?如果没有,为什么我不应该只使用常规 I/O?
0赞
Petr Skocik
11/2/2023
@Marco 如果你总是在写大块数据,那么你不妨跳过中间人。然后你基本上是在做你自己的缓冲。
评论