“最佳”IO缓冲 - 程序员或内核的任务?

时间:2012-12-22 13:11:22

标签: c++ performance io kernel buffer

我的任务很简单:在Linux上用C ++读取和解析一个大文件。有两种方法:

  1. 逐字节解析。

    while(/*...*/) {
            ... = fgetc(...);
            /* do something with the char */
    }
    
  2. 缓冲区解析缓冲区。

    while(/*...*/) {
            char buffer[SOME_LARGE_NUMBER];
            fread(buffer, SOME_LARGE_NUMBER, 1, ...);
            /* parse the buffer */
    }
    
  3. 现在,逐字节解析对我来说更容易(不检查​​缓冲区有多满等)。但是,我听说读大片更有效率。

    哲学是什么? “最佳”缓冲是内核的任务,所以当我调用fgetc()时它已被缓冲了吗?或者是否建议我处理它以获得最佳效率?

    此外,除了所有哲学:Linux上的现实是什么?

5 个答案:

答案 0 :(得分:11)

无论fgetc()的性能或底层缓冲如何,为所需的每个字节调用一个函数,而不是有一个适当大小的缓冲区来迭代,这是内核无法帮助你的开销。

我为我的本地系统做了一些快速而肮脏的时间(显然是YMMV)。

我选择了一个~200k的文件,并对每个字节求和。我做了20000次,在使用fgetc()阅读和使用fread()阅读之间每1000个周期交替进行。我将每1000个循环计时为一个整体。我编译了一个发布版本,并启用了优化。

fgetc()循环变体始终 45x 慢于fread()循环。

在评论中提示后,我还比较了getc(),并且还改变了stdio缓冲区。表现没有明显变化。

答案 1 :(得分:3)

stdio 缓冲区不是内核的一部分。它是用户空间的一部分。

但是,您可以使用setbuf影响该缓冲区的大小。当该缓冲区不够满时, stdio 库将通过发出 read 系统函数来填充它。

因此,使用 fgetc fread 在内核和用户之间切换的条款无关紧要。

答案 2 :(得分:1)

无所谓,真的。即使从SSD中,I / O开销也使得缓冲所花费的时间相形见绌。当然,它现在是微秒而不是毫秒,但函数调用是以纳秒为单位测量的。

答案 3 :(得分:1)

fgetc缓慢的原因不是函数调用的数量,而是系统调用的数量。 fgetc通常以int fgetc(FILE *fp) { int ch; return (fread(&ch,1,1,fp)==1?ch:-1); }

的形式实施

即使fread本身可以缓冲64k或1k,系统调用开销也会产生差异,例如。

 int fgetc_buffered(FILE *fp) {
     static int head=0,tail=0; 
     static unsigned char buffer[1024];
     if (head>tail) return buffer[tail++];
     tail=0;head=fread(buffer,1,1024,fp);
     if (head<=0) return -1;
     return buffer[tail++];
 }

答案 4 :(得分:0)

stdio例程执行用户空间缓冲。当你调用getc,fgetc,fread时,它们从stdio用户空间缓冲区中获取数据。当缓冲区为空时,stdio将使用内核读取调用来获取更多数据。

设计文件系统的人都知道磁盘访问(主要是搜索)非常昂贵。因此,即使stdio使用512字节的块大小,文件系统也可能使用4 KB的块大小,内核将一次读取4 KB的文件。

通常内核会在读取后启动磁盘/网络请求。对于磁盘,如果它看到你按顺序读取文件,它将开始提前读取(在你要求之前获取块),这样数据就可以更快地获得。

内核也会将文件缓存在内存中。因此,如果您正在读取的文件适合内存,则在一次运行程序后,该文件将保留在内存中,直到内核决定最好缓存您正在引用的其他文件。

使用mmap不会获得内核预读的好处。