【问题标题】:Serializing a Dictionary to disk?将字典序列化到磁盘?
【发布时间】:2014-01-21 19:59:15
【问题描述】:

我们有一个哈希表(特别是 C# Dictionary 类),它包含数千/数百万个 (Key,Value) 对,用于接近 O(1) 的搜索命中/未命中。

我们希望能够将此数据结构刷新到磁盘(序列化)并稍后再次加载(反序列化),以便保留字典的内部哈希表。

我们现在做什么:

  1. 从磁盘加载 => List<KVEntity>。 (KVEntity 是可序列化的。我们使用 Avro 进行序列化 - 如果需要,可以删除 Avro)
  2. 从数组 => 字典中读取每个 KVEntity。这重新生成字典/哈希表内部状态。
  3. 保存时,从字典中读入数组(通过myKVDict.Values.SelectMany(x => x) 读入新的List<KVEntity>
  4. 我们将数组 (List<KVEntity>) 序列化到磁盘以保存原始数据

请注意,在我们的保存/恢复过程中,我们会丢失内部 tashtable/字典状态,并且每次都必须重建它。

我们希望直接序列化到字典(包括它的内部“实时”状态),而不是只为磁盘 i/o 使用中间数组。我们该怎么做?

一些伪代码:

// The actual "node" that has information. Both myKey and myValue have actual data work storing
public class KVEntity
{
    public string myKey {get;set;}
    public DataClass myValue {get;set;}
}

// unit of disk IO/serialization
public List<KVEntity> myKVList {get;set;} 

// unit of run time processing. The string key is KVEntity.myKey
public Dictionary<string,KVEntity> myKVDict {get;set;} 

【问题讨论】:

标签: c# serialization dictionary


【解决方案1】:

存储 Dictionary 实例的内部状态是不好的做法 - OOP 的一个关键原则是封装:内部实现细节故意对消费者隐藏。

此外,Dictionary 使用的映射算法可能会在不同版本的 .NET Framework 之间发生变化,特别是考虑到 CIL 程序集被设计为向前兼容(即针对 .NET 2.0 编写的程序通常适用于 .NET 框架)。 NET 4.5)。

最后,序列化字典的内部状态并没有真正的性能提升。使用定义明确的文件格式并注重可维护性而不是速度要好得多。此外,如果字典包含“数千”个条目,那么我认为应该在 15 毫秒内从磁盘加载(假设你有一个有效的磁盘格式)。最后,针对 RAM 优化的数据结构不一定能在顺序读取/写入更好的磁盘上运行良好。

您的帖子非常坚持使用字典的内部状态,但您现有的方法似乎很好(尽管它可以进行一些优化)。如果您透露更多细节,我们可以帮助您加快进度。

优化

我在现有实现中看到的主要问题是与数组和列表之间的转换,鉴于Dictionary 是可直接枚举的,因此这是不必要的。

我会这样做:

Dictionary<String,TFoo> dict = ... // where TFoo : new() && implements a arbitrary Serialize(BinaryWriter) and Deserialize(BinaryReader) methods

using(FileStream fs = File.OpenWrite("filename.dat"))
using(BinaryWriter wtr = new BinaryWriter(fs, Encoding.UTF8)) {

    wtr.Write( dict.Count );

    foreach(String key in dict.Keys) {

        wtr.Write( key );
        wtr.Write('\0');
        dict[key].Serialize( wtr );
        wtr.Write('\0'); // assuming NULL characters can work as record delimiters for safety.
    }
}

假设您的 TFoo 的 Serialize 方法很快,我真的认为您不会获得比这种方法更快的速度。

实现反序列化器对读者来说是一个练习,但应该是微不足道的。请注意我如何将字典的大小存储到文件中,因此返回的字典可以在创建时设置为正确的大小,从而避免@spender 在他的评论中描述的重新平衡问题。

【讨论】:

  • 虽然没有回答,但我真的很喜欢你的推理,所以 +1。在将其标记为答案之前,让我仔细研究一下。您在谈论的其他优化是什么?好奇!
【解决方案2】:

因此,鉴于 Dai 的推理,我们将坚持我们现有的策略,并且我们要维护 C# 和 Java 兼容性(这意味着 C# 字典的额外树状态位无论如何都会被丢弃在 Java 端,这将像现在一样只加载节点数据)。

对于仍然对此感兴趣的后来读者,我找到了一个非常好的response here,它在一定程度上回答了所提出的问题。一个关键的区别是这个答案是针对B+ Trees,而不是Dictionaries,尽管在实际应用中这两个数据结构在性能上非常相似。 B+ 树的性能比常规树(如二叉树、红黑树、AVL 等)更接近字典。具体来说,字典提供接近 O(1) 的性能(但没有“从范围中选择”的能力),而 B+ 树具有 O(logb(X)),其中 b = 基数通常很大,这使得它们与常规树相比具有非常高的性能,其中 b =2。为了完整起见,我将其复制粘贴到此处,但所有功劳归 csharptest.net 用于 B+ 树代码、测试、基准测试和编写。

为了完整起见,我将在此处添加我自己的实现。

【讨论】:

    猜你喜欢
    • 2016-12-01
    • 1970-01-01
    • 2016-01-03
    • 1970-01-01
    • 2011-12-14
    • 2022-01-10
    • 1970-01-01
    • 2018-02-03
    • 1970-01-01
    相关资源
    最近更新 更多