【问题标题】:Fast and frequent file access while executing C++ code执行 C++ 代码时快速且频繁地访问文件
【发布时间】:2019-07-07 19:35:05
【问题描述】:

我正在寻找有关如何最好地实现我的代码以满足以下要求的建议。在执行我的 c++ 代码期间,我经常需要访问存储在字典中的数据,而字典本身存储在文本文件中。该字典包含 1 亿个条目,并且在任何时间点,我的代码都会查询与这 1 亿个条目中的某个特定条目相对应的数据。进行这些查询没有特定的模式,而且在程序执行的生命周期内,并非字典中的所有条目都被查询。此外,字典在程序的生命周期内将保持不变。每个条目对应的数据并非都是相同的长度。我的字典的文件大小约为 24 GB,而我只有 16 GB 的 RAM 内存。我需要我的应用程序非常快,所以我想知道如何最好地实现这样的系统,以便最大限度地减少读取访问时间。

我也是创建字典的人,所以我可以灵活地将我的字典分成几个较小的卷。在考虑我能做什么的同时,我想出了以下几点,但不确定是否好。

  1. 如果我从文件开头存储字典中每个条目的行偏移量,然后读取相应条目的数据,我可以直接跳转到相应的偏移量。有没有办法使用 say ifstream 来做到这一点,而无需遍历所有行直到偏移线?在网上快速搜索似乎表明至少使用 ifstream 是不可能的,还有其他方法可以做到吗?
  2. 另一个极端的想法是为字典中的每个条目创建一个文件,这样我就有 1 亿个文件。这种方法的明显缺点是打开和关闭文件流的开销。

总的来说,我不相信我想到的任何一种方法都是好的,所以我想要一些建议。

【问题讨论】:

  • 存储字节偏移而不是行号;然后您可以直接跳到文件中的所需位置。请参阅 seekgtellg。我可能会尝试在 RAM 中保留一个哈希表,从键的哈希到相应的文件偏移量。
  • 您可以考虑使用内存映射文件 - boost 具有多平台实现。至于性能 - 始终配置您的解决方案,
  • 实现 trie 可能会减少内存占用。减少将取决于有多少单词以相同的前缀开头。对于完整的内存解决方案而言,这可能会或可能不会足够减少。
  • 这听起来像是数据库的工作。有什么理由不使用?
  • @marcinj 谢谢你的建议。我想知道 Igor 建议的内存映射和 seekg 调用之间的权衡是什么。快速搜索似乎向我暗示内存映射在内部使用 seekg 调用。但是,在 seekg 上使用内存映射有什么方便吗?如果您认为这可以保证关于堆栈交换的单独问题,我可以创建一个。

标签: c++ file c++11 file-access


【解决方案1】:

好吧,如果您只需要键值访问,并且数据大于内存容量,那么答案是 NoSQL 数据库。这意味着键和任意值的哈希类型索引。如果您没有其他约束,例如来自多个客户端的并发访问或扩展的可扩展性,您可以自己动手。对于自定义 NoSQL 数据库,最重要的问题是预期的键数,这些键会给出索引文件的大​​小。您可以找到相当好的散列算法,并且必须在更大的索引文件和更高的冲突风险之间做出决定。无论如何,除非您想使用 tera 字节的索引文件,否则您的代码必须为可能的冲突做好准备。

详细的示例解释远远超出了我可以在 SO 答案中写的内容,但它应该为您提供一个起点。

下一个优化将是应该在内存中缓存的内容。这取决于您期望查询的方式。如果不可能多次查询同一个键,您可能只依赖操作系统和文件系统缓存,内存映射文件会略有改进,否则缓存(索引和/或值)是有意义的。在这里您可以再次选择并实现缓存算法。

或者如果您认为它太复杂而收效甚微,您可以搜索一个免费的 NoSQL 数据库是否可以满足您的要求...

【讨论】:

    【解决方案2】:

    一旦您决定使用磁盘数据结构,它就不再是一个 C++ 问题,而是一个系统设计问题。你想实现一个基于磁盘的字典。 从现在开始,您应该考虑以下因素 - 您的磁盘参数是什么?是固态硬盘吗?硬盘?你每秒的平均查找率是多少?您的Lookup() 方法的延迟时间为 20 微秒 - 10 毫秒吗?

    磁盘字典需要随机磁盘搜索。这种寻道对于 SSD 有几十微秒的延迟,对于 HDD 有 3-10 毫秒的延迟。此外,您每秒可以进行多少次此类搜索是有限制的。例如,您可以阅读this article。 CPU 不再是瓶颈,IO 变得重要。

    如果你想追求这个方向 - 有最先进的 C++ 库可以为你提供on-disk key-value store(不需要进程外数据库),或者你可以自己做一些简单的事情。

    如果您的应用程序是批处理而不是服务器/UI 程序,即您有另一个有限的项目流要加入您的字典,那么我建议您阅读外部算法,如 Hash Join 或 MapReduce。在这些情况下,可以以这样的方式组织您的数据,而不是拥有 1 个 24GB 的巨大字典,您可以拥有 10 个大小为 2.4GB 的字典并顺序加载每个字典并加入。但为此,我需要了解您要解决什么样的问题。

    总而言之,您需要先设计系统,然后再编写解决方案。使用 cmets 中提到的 mmap 或尝试或其他技巧是局部优化(如果有的话),它们不太可能改变游戏规则。在进行粗略计算以了解主要方向之前,我不会急于探索它们。

    【讨论】:

      猜你喜欢
      • 2013-12-15
      • 1970-01-01
      • 1970-01-01
      • 2015-11-04
      • 2023-03-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多