【问题标题】:Efficient way to write results to file during the computational experiment在计算实验期间将结果写入文件的有效方法
【发布时间】:2012-06-06 16:20:16
【问题描述】:

我有一个软件可以执行一组实验 (C++)。 如果不存储结果,所有实验都需要一分钟多一点的时间。 生成的总数据量为 2.5 GB,太大了,无法在实验结束前存储在内存中,然后再写入文件。 所以我把它们写成块。

for(int i = 0; i < chunkSize;i++){
    outfile << results_experiments[i] << endl;
}

在哪里 ofstream outfile("数据"); 并且 outfile 只在最后关闭。

但是,当我将它们写入 4700 KB 的块(实际上是 4700/Chunksize = results_experiments 元素的大小)时,实验需要的时间大约长 50 倍(超过一个小时......)。这是不可接受的,并且使我之前的优化尝试看起来相当愚蠢。特别是因为这些实验再次需要使用许多不同的参数设置等进行。(至少 100 次,但最好更多)

具体我的问题是:

  • 理想的块大小是多少?

  • 有没有比我目前写入数据的方式更有效(或效率非常低的方式)?

基本上:帮助我尽可能少地引入文件 IO 开销..

我认为应该可以更快地完成此操作,因为复制(写入和读取!)生成的文件(相同大小)需要我不到一分钟的时间..

代码应该完全独立于平台,并且不使用任何(非标准)库(我可以为单独的平台提供单独的版本和更复杂的安装说明,但这很麻烦..) 如果在没有平台/库依赖(并且可能有)的情况下将总实验时间控制在 5 分钟以下是不可行的,我会认真考虑引入这些。 (平台是windows,但至少应该有一个简单的linux端口)

感谢您的努力。

【问题讨论】:

  • 每个endl 都会刷新缓冲区。你不想要那个。使用&lt;&lt; '\n'
  • 没有人能告诉你理想的 I/O 块大小是多少——这取决于太多因素,包括硬件因素。进行试验,直到找到适合您系统的那个。您仍然可以通过重叠计算和 I/O 来隐藏一些 I/O 延迟 - 在当前计算正在进行时写入前一个计算的结果。
  • 但是,我们可以告诉您,大多数 32 位窗口在 4k 的块大小下都可以很好地工作。
  • 尝试fprintf 而不是 iostreams。 iostream 非常慢。 stackoverflow.com/questions/4340396/…

标签: c++ c performance file file-io


【解决方案1】:

对于初学者来说,不为每个块刷新缓冲区似乎是个好主意。似乎也可以异步执行 IO,因为它完全独立于计算。您还可以使用mmap 来提高文件 I/O 的性能。

【讨论】:

  • "platform is Windows" 似乎排除了使用mmap
  • @JerryCoffin 我确信 windows 有类似的东西。似乎还有一个可移植的 Boost 实现。我从来没有使用过它,也无法评论它。
  • Windows 有 CreateFileMapping 和 MapViewOfFile,但它们很少能提高 I/O 性能。
  • 您的评论,删除 endl 并将其替换为 '\n',让我的速度提高了 4 倍。我将研究二进制和多线程。
  • 我决定让它保持同步。我不认为它会对总运行时间产生巨大影响。由于我所有的计算只需要 1 分半钟,即使我可以在文件写入的同时完成它们,总运行时间最多也会减少 1 分半钟。如果我在这里错了,请纠正我。
【解决方案2】:

如果输出不必是人类可读的,那么您可以研究二进制格式。以二进制格式存储数据比文本格式占用更少的空间,因此需要更少的磁盘 i/o。但是如果数据都是字符串,那差别不大。因此,如果您尽可能多地写出数字而不是格式化文本,您可以获得很大的收益。

但是我不确定是否/如何使用 STL iostreams 完成此操作。 C 风格的方式是使用fopen(..., "wb")fwrite(&amp;object, ...)

我认为boost::Serialisation 可以使用&lt;&lt; 运算符进行二进制输出。

另外,你能减少你写的数量吗?例如没有格式或多余的文本,只是最低限度。

【讨论】:

  • outfile.write(static_cast&lt;const char*&gt;(results_experiments[i]), sizeof(results_experiments[i]));
  • 在我的情况下,使用二进制实际上使它变慢了。我想我可能会写更多而不是更少。 Result_experiments[i] 是一个整数(只有 1-3 位长),假设每个整数 4 个字节,字符串中每个字符 1 个字节是有意义的。
【解决方案3】:

写入ofstream时endl是否刷新缓冲区取决于实现--

您也可以尝试增加 ofstream 的缓冲区大小

char *biggerbuffer = new char[512000];

  outfile.rdbuf()->pubsetbuf(biggerbuffer,512000);

pubsetbuf 的可用性可能因您的 iostream 实现而异

【讨论】:

  • AFAIK 将endl 插入basic_ostream&lt;T&gt; 将始终调用flush
  • 更大的缓冲区根本没有影响我的运行时间,我尝试了许多不同的格式,从适度到巨大。没有区别,但内存使用率更高。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-08
  • 1970-01-01
  • 2011-04-20
  • 2012-05-14
  • 2016-11-09
  • 2011-06-06
相关资源
最近更新 更多