大型数据集计算问题的 I/O 策略?

计算科学 Python C 效率
2021-11-29 00:05:31

我的研究小组专注于分子动力学,它显然可以生成千兆字节的数据作为单个轨迹的一部分,然后必须对其进行分析。

我们关心的几个问题涉及数据集中的相关性,这意味着我们需要跟踪内存中的大量数据并对其进行分析,而不是使用更顺序的方法。

我想知道将大型数据集的 I/O 处理为脚本的最有效策略是什么。我们通常使用基于 Python 的脚本,因为它比 C 或 Fortran 编写文件 I/O 的痛苦要小得多,但是当我们有数千万或数亿行需要处理时,最好的方法是什么就不是那么清楚了. 我们应该考虑在 C 中做代码的文件输入部分,还是另一种策略更有用?(简单地将整个数组预加载到内存中会比一系列顺序读取“块”(以兆字节为单位)更好吗?

一些附加说明:

  • 我们主要是在寻找用于后处理的脚本工具,而不是“在线”工具——因此使用了 Python。

  • 如上所述,我们正在进行 MD 模拟。一个感兴趣的话题是扩散计算,为此我们需要获得爱因斯坦扩散系数:

    D=16limΔt(x(t+Δt)x(t))2
    这意味着我们确实需要在开始计算之前将所有数据加载到内存中——所有数据块(单个时间的记录)将相互交互。

3个回答

我以前不得不处理过类似的问题,我最喜欢的解决方案是使用Memory-mapped I/O,尽管在 C 中......

它背后的原理很简单:不是打开一个文件并从中读取,而是直接将它加载到内存中并像访问一个巨大的数组一样访问它。使其高效的技巧是操作系统实际上并不加载文件,它只是将它视为需要加载的换出内存。当您访问文件中的任何给定字节时,文件该部分的内存页面将被交换到内存中。如果您继续访问文件的不同部分并且内存变得紧张,那么较少使用的部分将自动换出 - 自动!

快速的 Google 搜索告诉我,这也适用于 Python:16.7。mmap — 内存映射文件支持,但我对 Python 的了解还不够,无法判断它是否真的是一回事。

我假设您的问题来自于 I/O 在您的整个分析中导致显着开销的观察。在这种情况下,您可以尝试将 I/O 与计算重叠。

一个成功的方法取决于您如何访问数据,以及您对该数据执行的计算。如果您可以识别模式,或者事先知道对数据不同区域的访问,您可以尝试在处理“当前块”的同时在后台预取“下一个块”数据。

举个简单的例子,如果您只遍历文件一次并处理每一行或每组行,则可以将流划分为行块(或 MB)。然后,在块的每次迭代中,您可以在处理块 i 时加载块 i+1。

您的情况可能更复杂,需要更多涉及的解决方案。无论如何,这个想法是在处理器有一些数据要处理的时候在后台执行 I/O。如果您提供有关您的具体问题的更多详细信息,我们也许可以更深入地研究它;)

----提供更多细节后的扩展版本----

我不确定我是否理解这个符号,但正如你所说,这个想法是一种全方位的互动。您还提到数据可能适合 RAM。然后,我将首先测量加载所有数据的时间和执行计算的时间。现在,

  • 如果 I/O 的百分比很低(低到你不关心开销,不管它是什么:0.5%、2%、5%,...),那么只需使用简单的方法:加载数据一次,并计算。您将为研究中更有趣的方面节省时间。

  • 如果你负担不起开销,你可能想看看佩德罗的建议。请记住 Aron Ahmadia 提到的内容,并在进行全面实施之前对其进行测试。

  • 如果前面的不令人满意,我会去一些核心实现[1]。由于您似乎正在对 n 个数据执行计算,因此有希望 :) 一些伪代码(假设您的分析结果适合 RAM):n2n

    加载块 1 和块 2
    对于块 i = 1 到 n
        异步加载块 i+1
        对于 j = i+1 到 n 中的块
            异步加载块 j+1
            使用块 i、j 计算(* 对于第一次迭代,这些是预加载的块 1 和 2 *)

注意:这是一种快速而肮脏的伪代码,需要调整索引。

为了实现这一点,通常使用所谓的双缓冲粗略地说:将内存划分为两个工作区;当数据在后台加载到工作区 1 中时,处理器正在计算工作区 2 中的数据。在每次迭代中,交换角色。

很抱歉,我现在无法提供一个好的参考。

[1] 核外算法结合了一些机制来(有效地)处理驻留在磁盘上的数据。它们被称为核外而不是核内(“in-RAM”)。

也许您可以在文件 I/O 部分中使用 Cython 并将这部分转换为 C 代码?