如何用boost :: iostreams更改缓冲区大小?

问题描述:

我的程序并行读取数十个非常大的文件,每次只读一行。看起来主要的性能瓶颈是硬盘从文件到文件的寻道时间(尽管我不完全确定如何验证这一点),所以我认为如果我可以缓冲输入,速度会更快。如何用boost :: iostreams更改缓冲区大小?

我使用C++这样的代码,通过升压来阅读我的文件:: iostreams的“流过滤”:

input = new filtering_istream; 
input->push(gzip_decompressor()); 
file_source in (fname); 
input->push(in); 

按照documentationfile_source没有任何办法来设置缓冲区的大小,但filtering_stream ::推似乎:

void push(const T& t, 
    std::streamsize buffer_size, 
    std::streamsize pback_size); 

所以,我想input->push(in, 1E9)其实我心里想程序的内存使用量飙升,但速度一点都没有改变。

我只是错了,读缓冲会提高性能?或者我做错了吗?我可以直接缓冲一个file_source,还是我需要创建一个filtering_streambuf?如果后者,这是如何工作的?该文档并不完全是例子。

你也应该剖析它,看看瓶颈在哪里。

也许它在内核中,也许你的硬件的限制。直到你分析它,发现你在黑暗中磕磕绊绊。

编辑:

好,更完整的答案这一次,然后。根据Boost.Iostreams文档basic_file_source仅仅是围绕std::filebuf的封装,其依次建立在std::streambuf上。引用文档:

以只读模式打开的std :: basic_filebuf的CopyConstructible和Assignable包装器。

streambuf确实提供了一种方法pubsetbuf(不是最好的参考也许,但在第一谷歌翻起),它可以,显然,用来控制所述缓冲器大小。

例如:

#include <fstream> 

int main() 
{ 
    char buf[4096]; 
    std::ifstream f; 
    f.rdbuf()->pubsetbuf(buf, 4096); 
    f.open("/tmp/large_file", std::ios::binary); 

    while(!f.eof()) 
    { 
     char rbuf[1024]; 
     f.read(rbuf, 1024); 
    } 

    return 0; 
} 

在我的测试(优化过,虽然)事实上,我更糟糕的表现与4096个字节的缓冲区比16个字节的缓冲区,但情况因人而异 - 一个很好的例子,为什么你总是应该个人资料首先:)

但是,正如你所说,basic_file_sink不提供任何手段来访问它,因为它隐藏其底filebuf在其private part

如果你认为这是错误的,你可以:

  1. 敦促加速开发人员能够将这样的功能,使用邮件列表或TRAC。
  2. 构建您自己的filebuf包装,它确实暴露了缓冲区大小。本教程中有一个section,它解释了编写自定义源可能是一个很好的起点。
  3. 根据任何内容编写自定义源代码,完成所有您喜欢的缓存。

请记住,您的硬盘驱动器以及内核已经对文件读取进行了缓存和缓冲,我认为您不会从缓存中获得更多性能提升。

而最后,关于剖析的一个词。有很多功能强大的分析工具可用于Linux,我甚至不知道其名称中的一半,但例如有iotop,这是一种整洁,因为它非常简单易用。它非常像*,但是却显示了与磁盘相关的指标。例如:

Total DISK READ: 31.23 M/s | Total DISK WRITE: 109.36 K/s 
TID PRIO USER  DISK READ DISK WRITE SWAPIN  IO> COMMAND   
19502 be/4 staffan 31.23 M/s 0.00 B/s 0.00 % 91.93 % ./apa 

告诉我,我的程序花费了超过90%的时间来等待IO,即它的IO限制。如果你需要更强大的功能,我相信谷歌可以帮助你。

请记住,在热或冷高速缓存上进行基准测试会极大地影响结果。

+0

也许这是另一个帖子的问题,但我将如何描述它?我知道如何使用gprof,但它只告诉我CPU时间,在这里我很确定瓶颈是磁盘I/O。 或者如果有人能告诉我如何正确设置缓冲区大小,我可以试试看看是否有帮助。 – user387250 2010-07-10 02:07:19

+0

@jwfoley:我喜欢[Valgrind's](http://valgrind.org/)callgrind分析器。就我的经验而言(读作:我不能保证任何事情),它也报告了在内核调用中花费的时间,这是我永远无法获得gprof所做的事情。例如,我用它来剖析一个使用OpenGL的应用程序,并正确报告在视频驱动程序代码中花费的时间。它非常易于使用(valgrind --tool = callgrind ./your-app)。使用[KCachegrind](http://kcachegrind.sourceforge.net/html/Home.html)解释结果。唯一的问题是你的应用程序在分析时会运行20次左右。 – Staffan 2010-07-10 02:17:35

+0

@Staffan:好的,我试过callgrind + KCachegrind,我对Profiler印象深刻,但我仍然不知道自己在找什么。结果看起来很像gprof's。称为T.3577的东西有很高的“包含”。但低“自我”;大部分时间似乎都花在std :: basic_ios上。也许这就是磁盘I/O? 我仍然想回答我如何设置缓冲区大小的原始问题。如果这很容易,那么我可以尝试一下,看看它是否有帮助,但无论如何它都是有用的。 – user387250 2010-07-12 21:41:50