PyTables处理大小比内存大小大许多倍的数据

时间:2013-02-20 15:44:33

标签: python io hdf5 pytables

我正在尝试理解PyTables如何管理大小大于内存大小的数据。 以下是PyTables代码中的注释(link to GitHub):

# Nodes referenced by a variable are kept in `_aliveNodes`.
# When they are no longer referenced, they move themselves
# to `_deadNodes`, where they are kept until they are referenced again
# or they are preempted from it by other unreferenced nodes.

_getNode方法中也可以找到有用的评论 似乎PyTables有非常智能的IO缓冲系统,据我所知,它将用户在快速RAM中引用的数据存储为“aliveNodes”,在之前保持引用,当前未引用的数据为“deadNodes”,以便在需要时快速“恢复”它,以及如果请求的密钥在死或活类别中都不存在,则从磁盘读取数据。

我需要一些关于PyTables在处理大于可用内存的数据时如何处理情况的专业知识。我的具体问题:

  1. deadNode / aliveNode系统如何工作(常见图片)?
  2. 如果我正确的话,aliveNodes / deadNodes之间的关键区别是什么,而它们都代表存储在RAM中的数据?
  3. 可以手动调整缓冲RAM的限制吗?在评论下方,有代码从params['NODE_CACHE_SLOTS']读取值。它可以以某种方式由用户指定吗?例如,如果我想为其他需要内存的应用程序留下一些RAM?
  4. 在大量工作时,PyTables在什么情况下会崩溃或显着减速 数据的?在我的情况下,可以超过内存100倍,在这种情况下常见的陷阱是什么?
  5. PyTables在大小意义,数据结构以及数据操作方面的用途是什么?它们被认为是“正确”的数据以实现最佳性能?
  6. Docs suggests在每个基本.flush()周期后使用.append()。这个周期实际上可以有多长?我正在执行一些基准测试,比较SQLite和PyTables如何处理从大型CSV文件创建一个包含键值对的巨大表格。当我使用.flush()时,主循环中的频率较低,PyTables获得了巨大的加速。那么 - 对于.append()相对较大的数据块是否正确,然后使用.flush()

3 个答案:

答案 0 :(得分:2)

内存结构

从未使用pytables但查看源代码:

class _Deadnodes(lrucacheExtension.NodeCache):
    pass

所以看起来_deadnodes是使用LRU缓存实现的。 LRU ==“最近最少使用”,这意味着它将首先丢弃最少使用的节点。来源是here

class _AliveNodes(dict):
    ...

它们用作节目的自定义字典,正在程序中运行并实际表示。

非常简化的示例(节点是字母,缓存中的数字表示条目的陈旧程度):

memory of 4, takes 1 time step
cache with size 2, takes 5 times steps
disk with much much more, takes 50 time steps

get node A //memory,cache miss load from disk t=50
get node B // "" t=100
get node C // "" t=150
get node D // "" t=200
get node E // "" t=250
get node A //cache hit load from cache t=255
get node F //memory, cache miss load from disk t=305
get node G //memory, cache miss load from disk t=355
get node E // in memory t=356 (everything stays the same)

t=200              t=250              t=255
Memory    CACHE    Memory    CACHE    Memory    CACHE
A                  E         A0       E         B0
B                  B                  A
C                  C                  C
D                  D                  D

t=305              t=355              
Memory    CACHE    Memory    CACHE
E         B1       E         G0
A         C0       A         C1
F                  F
D                  G

正如您在现实生活中所知道的那样,这些结构非常庞大,访问它们所需的时间是在总线周期中,所以1 /(你电脑的时钟)。

相比之下,访问元素所需的时间是相同的。它在内存中几乎可以忽略不计,对缓存来说更多一点,而对于磁盘则更多。从磁盘读取是整个过程中最长的部分。磁盘和手臂需要移动等。这是一个物理过程而不是电子过程,因为它不是以光速发生的。

在pytables中,他们做了类似的事情。他们在Cython中编写了自己的缓存算法,这是活动节点(内存)和完整数据(磁盘)之间的中间人。如果命中率太低,则看起来缓存将被关闭,并且在一定数量的周期后它将再次打开。

parameters.py中,DISABLE_EVERY_CYCLEENABLE EVERY_CYCLELOWEST_HIT_RATIO变量用于定义LOWEST_HIT_RATIO下禁用之后的周期数以及等待重新生成的周期数 - 启用。不鼓励改变这些值。

您应该从中获取的主要内容是,如果您需要对大型数据集进行处理,请确保它们位于相同的节点上。如果你可以逃脱它,读入一个块,对该chuck进行处理,获得结果,然后加载另一个块。如果加载块A,获取另一个块B,然后再次加载块A,这将导致最大的延迟。仅一次操作一个数据块,并将访问和写入保持在最低限度。一旦值在_alivenodes中,就可以快速修改它,_deadnodes稍微慢一点,而且速度也慢得多。

NODE_CACHE_SLOTS

params['NODE_CACHE_SLOTS']定义死节点集的大小。将其追溯到parameters.py,默认为64.它表示您可以尝试不同的值并报告回来。您可以更改文件中的值,也可以执行以下操作:

import parameters
parameters.NODE_CACHE_SLOTS = # something else

这仅限制缓存中保留的节点数。过去你受到python堆大小的限制,设置为this

附加/冲洗

对于appendflush确保将行输出到表中。使用此数据移动的数据越多,数据从内部缓冲区移动到数据结构所需的时间就越长。它使用其他处理代码调用H5TBwrite_records函数的修改版本。我猜测调用的长度决定了输出周期的长短。

请记住,这完全来自源代码,并没有考虑他们试图做的任何其他魔法。我从未使用过pytables。从理论上讲,它不应该崩溃,但我们不会生活在理论世界中。

编辑:

实际上我自己发现了对pytables的需求,我在他们的常见问题中遇到this question,可能会回答你的一些问题。

感谢您向我公开pytables,如果我在研究这个问题之前遇到过.h5个文件,我就不知道该怎么做。

答案 1 :(得分:1)

我不是PyTable 1 的专家,但很可能像swap memory一样。

aliveNodes存在于RAM中,而deadNodes可能存储在hdf5文件(PyTables使用的二进制文件格式)的磁盘上。 每次需要访问一段数据时,它都需要在RAM中。所以PyTable检查它是否已经存在(aliveNodes)并返回给你(如果是)。否则,它需要恢复数据所在的deadNode。由于RAM是有限的,它可能 kill (写入磁盘)未使用的aliveNode以预先建立一些空间。

此过程的原因当然是RAM的大小有限。 结果是,每次需要交换节点时,性能都会受到影响( kill 一个节点,复活另一个节点)。

为了优化性能,您应该尽量减少交换。例如,如果您的数据可以并行处理,则您可能只能加载每个节点一次。其他示例:假设您需要遍历巨大矩阵的每个元素,该矩阵被拆分为节点网格。然后你最好避免按行或逐列访问它的元素,而是逐个节点地访问它。

当然PyTable处理这个问题,所以你没有必要控制每个节点中的内容(但我鼓励你挖掘这个NODE_CACHE_SLOTS变量,至少要理解它是如何工作的)。但通常情况下,访问连续数据而不是分散在整个地方的数据会更快。与往常一样,如果时间性能是您的应用程序的重要问题,请分析您的代码。


1 翻译:我对PyTables几乎一无所知

答案 2 :(得分:0)

我也不是PyTable的专家,Simon似乎已经很好地介绍了交换内存的概念,但是如果你想要一个算法的具体例子来处理数据太大而不适合内存,我会建议看外部排序。

基本思想是:您无法将所有数据都放在内存中,但需要对其进行排序。但是,您可以在大小为k的块中将某些数据放入内存中。说有这样的块。

  • 将数据拆分为大小为k的块。
  • 对于每个块,将其带入内存并对其进行排序(例如使用quicksort或其他),然后将其排序版本写回磁盘。

现在,我们将j块排序数据我们想要合并成一个长整齐的数据。这个问题听起来像mergesort!所以,

  • 将每个j个排序块中的最小值带入内存
  • 找出这些j值中最小的一个。那是最小的数据!因此,将其作为排序数据集的开头写入磁盘。
  • 将新写入的值替换为其块中的下一个最小值到内存中(这是交换内存的'交换'位)。

现在,内存中的数据是最小的j,,除了我们已经写入到磁盘上最终排序数据集的数据。因此,如果我们重复该过程直到所有数据都写入最终集合,它将始终排序。

所以,这只是一个算法的例子,它使用内存交换来处理太大而无法容纳在内存中的数据。 PyTable的排序方法可能就是这些。

加分:Here some to链接{{3}}对外部排序的更多解释。