5

我正在开发一个非常占用 cpu 的科学软件(它的 proc 绑定),但它需要经常将数据写入磁盘(i/o 绑定)。

我正在为此(OpenMP)添加并行化,我想知道解决写入磁盘需求的最佳方法是什么。模拟没有理由在 HDD 上等待(这就是它现在正在做的事情)。

我正在为此寻找“最佳实践”,而速度是我最关心的(这些可能是非常长的模拟)。

谢谢~亚历克斯

第一个想法:

有一个单独的进程实际写入磁盘,因此模拟有两个进程:一个是 CPU 密集型(模拟),一个是 IO 密集型(写入文件)。这听起来很复杂。

可能是管道/缓冲区?我对这些有点陌生,所以也许这可能是一个可能的解决方案。

4

6 回答 6

5

我想说最好的方法是产生一个不同的线程来保存数据,而不是一个全新的进程;使用新流程时,您会遇到必须跨流程边界传递要保存的数据的麻烦,这带来了一系列新的困难。

于 2009-06-18T17:42:40.327 回答
3

想到的第一个解决方案几乎就是您所说的 - 在自己的进程中使用从 sim 到写入器的单向管道进行磁盘写入。作者确实尽可能快地写入(从管道中提取新数据)。这样做的问题是,如果 sim 比编写器领先太多,那么 sim 无论如何都会阻塞管道写入,并且它会在一次删除时绑定 I/O。

问题是,实际上你的模拟周期在它吐出结果之前是不完整的。

我想到的第二件事是使用非阻塞 I/O。每当 sim 需要写入时,它都应该通过非阻塞 I/O 来完成。在下一次需要写入时,它可以在开始新的 I/O 操作之前获取上一个 I/O 操作的结果(可能会导致一小段等待)。这使模拟尽可能与 I/O 并行运行,而不会让模拟远远领先于写入。

如果模拟处理周期不同(有时小于写入时间,有时更长),第一个解决方案会更好,因为平均而言,写入可能跟上 sim。

如果处理周期总是(或几乎总是)比写入时间短,那么你最好不要打扰管道而只使用非阻塞 I/O,因为如果你使用管道,它最终会填满无论如何,SIM卡都会挂断I/O。

于 2009-06-18T17:57:35.887 回答
2

如果您在程序中实现 OpenMP,那么最好使用并行部分的#pragma omp single#pragma omp master保存到文件。这些 pragma 只允许一个线程执行某事。因此,您的代码可能如下所示:

#pragma omp parallel
{
    // Calculating the first part
    Calculate();

    // Using barrier to wait all threads
    #pragma omp barrier

    #pragma omp master
    SaveFirstPartOfResults();

    // Calculate the second part
    Calculate2();

    #pragma omp barrier

    #pragma omp master
    SaveSecondPart();

    Calculate3();

    // ... and so on
}

这里的线程组将进行计算,但只有单个线程会将结果保存到磁盘。

它看起来像软件管道。我建议您考虑英特尔线程构建模块库中的 tbb::pipeline 模式。我可以向您推荐 http://cache-www.intel.com/cd/00/00/30/11/301132_301132.pdf#page=25上的软件管道教程。请阅读第 4.2 段。他们解决了这个问题:一个线程从驱动器读取,第二个线程处理读取的字符串,第三个线程保存到驱动器。

于 2009-06-20T14:10:09.413 回答
1

由于您受 CPU 和 IO 限制:让我猜猜:还有大量可用内存,对吧?

如果是这样,您应该将必须写入内存中的磁盘的数据缓冲到一定程度。写入大量数据通常比写入小块数据要快得多。

对于写作本身:考虑使用内存映射 IO。自从我进行基准测试以来已经有一段时间了,但上次我这样做的速度明显更快。

您也可以随时交易 CPU 与 IO。我认为您目前正在将数据写入某种原始的未压缩数据,对吗?如果您使用简单的压缩方案来减少要写入的数据量,您可能会获得一些 IO 性能。ZLIB 库非常易于使用,并且在最低压缩级别上压缩得非常快。这取决于数据的性质,但如果其中有很多冗余,即使是非常粗略的压缩算法也可以消除 IO 绑定问题。

于 2009-06-18T17:56:53.313 回答
0

一个线程连续执行计算密集型过程的一个步骤,然后将部分结果添加到部分结果队列中。另一个线程不断地从队列中删除部分结果并将它们写入磁盘。确保同步对队列的访问。队列是一种类似列表的数据结构,您可以在其中将项目添加到末尾并从前面删除项目。

于 2009-06-18T17:49:12.750 回答
0

让你的应用程序有两个线程,一个用于 CPU,一个用于硬盘。

让 CPU 线程将完成的数据推送到一个队列中,然后硬盘线程在数据进入时从中提取。

这样,CPU 只是摆脱数据并让其他人处理它,而硬盘驱动器只是耐心地等待其队列中的任何数据。

在实现方面,您可以将队列作为共享内存类型的对象,但我认为管道正是您要寻找的。CPU 只是在需要时写入管道。在硬盘方面,您只需读取管道,只要您获得有效数据,就从那里继续。

于 2009-06-18T18:01:42.323 回答