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

Gil*_*tes 10 python io hdf5 pytables

我试图理解PyTables如何管理大小大于内存大小的数据.这是PyTables代码中的注释(链接到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.
Run Code Online (Sandbox Code Playgroud)

_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建议.flush()在每个基本.append()周期后使用.这个周期实际上可以有多长?我正在执行一些基准测试,比较SQLite和PyTables如何处理从大型CSV文件创建一个包含键值对的巨大表格.当我使用时.flush(),主循环中的频率较低,PyTables获得了巨大的加速.那么 - 对于.append()相对较大的数据块是否正确,然后使用.flush()

Rau*_*fio 2

内存结构

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

class _Deadnodes(lrucacheExtension.NodeCache):
    pass
Run Code Online (Sandbox Code Playgroud)

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

class _AliveNodes(dict):
    ...
Run Code Online (Sandbox Code Playgroud)

他们将其用作程序中实际运行和表示的节点的定制字典。

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

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
Run Code Online (Sandbox Code Playgroud)

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

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

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

parameters.pyDISABLE_EVERY_CYCLE, 、ENABLE EVERY_CYCLELOWEST_HIT_RATIO变量用于定义 LOWEST_HIT_RATIO 下禁用后的周期数以及等待重新启用的周期数。不鼓励更改这些值。

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

NODE_CACHE_SLOTS

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

import parameters
parameters.NODE_CACHE_SLOTS = # something else
Run Code Online (Sandbox Code Playgroud)

这仅限制缓存中保存的节点数量。过去,你会受到 python 堆大小的限制,要设置它,请参阅this

追加/刷新

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

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

编辑:

实际上我自己也发现了对 pytables 的需求,我遇到了这个问题,这可能会解答您的一些担忧。

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