在生成数据时写入数据的最快方式

The fastest way to write data while producing it

本文关键字:数据 方式      更新时间:2023-10-16

在我的程序中,我正在模拟一个n体系统进行大量迭代。对于每次迭代,我生成一组6N个坐标,我需要将其附加到文件中,然后用于执行下一次迭代。该代码是用c++编写的,目前使用ofstreamwrite()方法在每次迭代时以二进制格式写入数据。

我不是这方面的专家,但我想改进这部分程序,因为我正在优化整个代码。我觉得在每个周期中与编写计算结果相关的延迟显著降低了软件的性能。

我很困惑,因为我没有实际并行编程和低级文件I/O的经验。我想到了一些我认为我可以实现的抽象技术,因为我是在为Unix操作系统的现代(可能是多核)机器编程:

  • 在n次迭代的块中写入文件中的数据(似乎有更好的方法来进行…)
  • 与OpenMP并行化代码(如何实际实现一个缓冲区,使线程适当同步,不重叠?)
  • 使用mmap(文件大小可能很大,按gb的顺序,这种方法足够健壮吗?)

然而,我不知道如何最好地实现它们并适当地组合它们。

当然,在每次迭代时写入一个文件是低效的,而且很可能减慢您的计算速度。(根据经验,这取决于你的实际情况)

必须使用生产者->消费者设计模式。它们将像传送带一样由一条队列连接起来。

  • 生产者将尽可能快地生产,只有当消费者无法处理它时才会减慢速度。
  • 消费者会尽可能快地"消费"。

通过拆分这两个进程,您可以更容易地提高性能,因为每个进程都更简单,并且来自另一个进程的干扰更少。

  • 如果生产者更快,你需要改进消费者,在你的情况下,以最有效的方式写入文件,最有可能的是(正如你所说的)
  • 如果消费者更快,你需要改进生产者,最有可能的是通过并行化它。

There isno need优化两者。只优化最慢的(瓶颈)。

实际上,您使用线程和它们之间的同步队列。关于实现提示,请看这里,特别是第18.12节"生产者-消费者模式"。

关于流管理,你必须通过选择一个"最大队列大小"来增加一点复杂性,并在队列没有足够的空间时让生产者等待。小心死锁,小心编码。(参见我给出的维基百科链接)

注意:使用boost线程是一个好主意,因为线程不是很可移植。(嗯,从c++ 0x开始,但是c++ 0x的可用性还不是很好)

最好将操作分成两个独立的进程:data-producingfile-writingData-producing将使用一些缓冲区进行迭代数据传递,file-writing将使用一个队列来存储写请求。然后,data-producing只会发布一个写请求并继续,而file-writing会在后台处理写操作。

本质上,如果数据生成的速度比存储的速度快得多,那么您很快就会将大部分数据保存在缓冲区中。在这种情况下,你的实际方法似乎是相当合理的,因为几乎没有什么可以通过编程来改善这种情况。

如果您不想在不同的线程中执行操作,您可以尝试使用aio_write(),它允许异步写入。从本质上讲,你给操作系统一个缓冲区来写,函数立即返回,并在你的程序继续时完成写,你可以稍后检查写是否已经完成。

这个解决方案仍然受到其他答案中提到的生产者/消费者问题的影响,如果你的算法产生数据的速度比写入数据的速度快,最终你将耗尽内存来存储算法和写入之间的结果,所以你必须尝试它,看看它是如何工作的。

"使用mmap(文件大小可能很大,以gb为数量级)是这样的方法是否足够稳健?)"

mmap是操作系统加载程序、共享库和页面/交换文件的方法——它和任何其他文件I/O一样健壮,而且通常性能更高。

但是在大多数操作系统上,在使用映射文件时扩展它的大小是不好/困难/不可能的。所以如果你知道数据的大小,或者你只是在阅读,那就太好了。对于不断添加的日志/转储,除非您知道最大大小,否则不太适合。