【问题标题】:Correct data structure to use for (this specific) expiring cache?用于(此特定)过期缓存的正确数据结构?
【发布时间】:2010-06-20 21:45:50
【问题描述】:

我需要从一个非常大、高度互连、数据相当本地化且读取成本相当高的数据集中读取数据。具体来说:

  1. 数据集的大小为 2gigs - 30gigs,因此我必须将文件的各个部分映射到内存中才能读取。与我在算法中所做的其他工作相比,这非常昂贵。通过分析,我发现大约 60% 的时间都花在了读取内存上,因此这是开始优化的正确位置。
  2. 在对这个数据集的一部分进行操作时,我必须跟踪其中的链接(想象它类似于链表),虽然不能保证这些读取几乎是连续的,但它们是相当本地化的。这意味着:
  3. 例如,我们一次操作 2 兆内存。如果你将 2 兆的数据读入内存,我随后要做的大约 40% 的读取将在相同的 2 兆内存中。大约 20% 的读取将是对其余数据的纯随机访问,另外 40% 很可能链接回指向该数据的 2meg 段。

从对问题的了解和分析来看,我相信在程序中引入缓存会有很大帮助。我想要做的是创建一个缓存,其中包含 N 块 X megs 内存(N 和 X 可配置,因此我可以调整它)我可以先检查它,然后再映射另一部分内存。此外,缓存中的时间越长,我们在短期内请求该内存的可能性就越小,因此最旧的数据将需要过期。

毕竟,我的问题很简单:什么数据结构最适合实现这种性质的缓存?

我需要快速查找以查看给定地址是否在缓存中。对于缓存的每一次“未命中”,我都会想让其中最旧的成员过期,并添加一个新成员。但是,我计划尝试调整它(通过更改缓存的数量),以使 70% 或更多的读取命中。

我目前的想法是使用 AVL 树(用于搜索/插入/删除的 LOG2 n)是最安全的(没有退化的情况)。我的另一个选择是稀疏哈希表,这样在最好的情况下查找将是 O(1)。从理论上讲,这可能会退化为 O(n),但实际上我可以将碰撞保持在较低水平。这里关心的是在哈希表中找到并删除最旧的条目需要多长时间。

有没有人对这里最好的数据结构有什么想法或建议?为什么?

【问题讨论】:

  • 您问题的某些部分让我想起了最近一篇题为“你做错了”的 ACM 队列 文章:queue.acm.org/detail.cfm?id=1814327。也许它会给你一些想法。无论如何,这都是一本有趣的书。
  • 您是否在 64 位架构上运行?如果是这样,为什么不只对整个数据集进行内存映射?
  • 两个原因:首先,它必须在 x86 上运行并且高效。在 amd64 上,这不是一个问题。其次,我实际上无法直接控制内存映射,但有机会为请求添加缓存......这比我在问题中简单描述的要复杂。

标签: c++ algorithm caching


【解决方案1】:

似乎您正在寻找LRU (Least Recently Used) 缓存:LRU cache design

【讨论】:

  • 我也是这么想的,请注意(即使链接的问题中没有提到)Boost.MultiIndex 可以真正实现这一点,如果我没记错的话,它会在文档中引用。
【解决方案2】:

如果您的算法中有 60% 是 I/O,我建议实际的缓存设计并不那么重要 - 任何类型的缓存都可以显着提高速度。

但是,设计在很大程度上取决于您用于访问块的数据。 String、int 等。如果你有一个 int,你可以在链表中做一个 hashmap,在缓存未命中时擦除后面,如果缓存命中则擦除然后推到顶部。

在许多实现中以不同的名称(最常见的是无序映射)提供散列图。 Boost 有一个,TR1 中也有一个,等等。hash_map 的一大优势是随着数量的增长减少性能损失,并且在键值方面具有更大的灵活性。

【讨论】:

  • 不幸的是,我们谈论的不是简单的数据。我正在查看的每个条目都是任意大小的结构。每个结构都是自我描述的,可以包含不同类型的数据,包括整数、字符串等。这就是为什么我基本上必须在内存级别实现缓存,而不是在数据级别,如果这有意义的话。感谢您提供有关 hashmap 的提示。这可能会节省我一些时间来实现自定义数据结构。
  • 天哪。这听起来是一项相当不错的工作。祝你好运。
【解决方案3】:

将缓存放入两个排序树中(AVL 或任何其他合理平衡的树实现都可以——最好使用库中的一个而不是创建自己的)。

一棵树应该按文件中的位置排序。这使您可以进行 log(n) 查找以查看您的缓存是否存在。

另一棵树应该按使用时间排序(可以用每次使用时递增一的数字表示)。当您使用缓存块时,您将其移除、更新时间并再次插入。这也需要 log(n)。当您错过时,删除树中最小的元素,并将新块添加为最大的。 (不要忘记将该块删除/添加到按位置文件树中。)

如果您的缓存中没有太多项目,您最好还是将所有内容存储在排序数组中(使用插入排序添加新元素)。将 16 个项目向下移动到数组中的一个位置非常快。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-04-05
    • 2015-11-19
    • 2012-02-05
    • 2013-06-18
    • 1970-01-01
    • 2015-11-04
    • 2011-10-16
    • 1970-01-01
    相关资源
    最近更新 更多