我正在尝试理解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在处理大于可用内存的数据时如何处理情况的专业知识。我的具体问题:
params['NODE_CACHE_SLOTS']
读取值。它可以以某种方式由用户指定吗?例如,如果我想为其他需要内存的应用程序留下一些RAM?.flush()
周期后使用.append()
。这个周期实际上可以有多长?我正在执行一些基准测试,比较SQLite和PyTables如何处理从大型CSV文件创建一个包含键值对的巨大表格。当我使用.flush()
时,主循环中的频率较低,PyTables获得了巨大的加速。那么 - 对于.append()
相对较大的数据块是否正确,然后使用.flush()
?答案 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_CYCLE
,ENABLE EVERY_CYCLE
和LOWEST_HIT_RATIO
变量用于定义LOWEST_HIT_RATIO下禁用之后的周期数以及等待重新生成的周期数 - 启用。不鼓励改变这些值。
您应该从中获取的主要内容是,如果您需要对大型数据集进行处理,请确保它们位于相同的节点上。如果你可以逃脱它,读入一个块,对该chuck进行处理,获得结果,然后加载另一个块。如果加载块A,获取另一个块B,然后再次加载块A,这将导致最大的延迟。仅一次操作一个数据块,并将访问和写入保持在最低限度。一旦值在_alivenodes
中,就可以快速修改它,_deadnodes
稍微慢一点,而且速度也慢得多。
params['NODE_CACHE_SLOTS']
定义死节点集的大小。将其追溯到parameters.py,默认为64.它表示您可以尝试不同的值并报告回来。您可以更改文件中的值,也可以执行以下操作:
import parameters
parameters.NODE_CACHE_SLOTS = # something else
这仅限制缓存中保留的节点数。过去你受到python堆大小的限制,设置为this。
对于append
,flush
确保将行输出到表中。使用此数据移动的数据越多,数据从内部缓冲区移动到数据结构所需的时间就越长。它使用其他处理代码调用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的块中将某些数据放入内存中。说有这样的块。
现在,我们将j块排序数据我们想要合并成一个长整齐的数据。这个问题听起来像mergesort!所以,
现在,内存中的数据是最小的j,,除了我们已经写入到磁盘上最终排序数据集的数据。因此,如果我们重复该过程直到所有数据都写入最终集合,它将始终排序。
所以,这只是一个算法的例子,它使用内存交换来处理太大而无法容纳在内存中的数据。 PyTable的排序方法可能就是这些。