关于c#:快速替换词典< TKey,TValue>

A faster replacement to the Dictionary<TKey, TValue>

我需要一个快速更换的System.Collections.Generic.Dictionary。我的申请应该很快。因此,替换应该支持:

  • 泛型
  • 添加
  • 得到
  • 包含

…就是这样。我不需要Linq的任何支持。而且应该很快。

一个简单的代码,比如:

1
2
3
4
5
6
7
Stopwatch stopWatch = Stopwatch.StartNew();

Dictionary<string, string> dictionary = new Dictionary<string, string>();
dictionary.Add("fieldName","fieldValue");
dictionary.Add("Title","fieldVaaaaaaaaaaaaaaaaalue");

Console.WriteLine(stopWatch.Elapsed);

…打印00:00:00.0001274,这对我来说是很长的时间,因为我的应用程序正在做许多其他的事情,其中一些是来自旧的慢库,我必须使用它们,并且不依赖于我。

关于如何实现更快的方法有什么想法吗?

谢谢您。


很可能您看到了JIT编译。在我的盒子上,我看到:

1
2
00:00:00.0000360
00:00:00.0000060

当我在同一进程中快速连续运行它两次时——而不是在调试器中。(确保您没有在调试器中运行它,或者它是一个无意义的测试。)

现在,测量任何微小的时间通常都是一个坏主意。您需要重复数百万次才能更好地了解它需要多长时间。

你有充分的理由相信它实际上会减慢你的代码——或者你是基于你最初的时间安排?

我怀疑你会发现比Dictionary快得多的东西,我会很惊讶地发现这是瓶颈。

编辑:我刚把一百万个元素添加到一个Dictionary中,其中所有的键都是现有的对象(数组中的字符串),重复使用相同的值(因为它是无关的),并且在构建时指定一百万的容量——在我两岁的笔记本电脑上大约需要0.15秒。

考虑到你已经说过你在应用程序的其他地方使用了一些"老的慢库",这真的可能成为你的瓶颈吗?请记住,其他库的速度越慢,改进的集合类所带来的影响就越小。如果字典的更改只占您整个应用程序时间的1%,那么即使我们可以提供即时字典,您也只能将应用程序的速度提高1%。

像往常一样,找一个剖析器-它会让你更好地了解你的时间去哪里。


我同意乔恩斯基特的假设,这是最有可能的JIT编译。

也就是说,我想在这里添加一些其他信息:

大多数与使用Dictionary有关的速度问题与字典的实现无关。Dictionary非常快,开箱即用。很难打败它。

与字典实例相关的速度问题几乎总是哈希代码实现问题。如果在使用Dictionary时出现速度问题,请重新访问在mycustomClass上定义的GetHashCode()实现。如果使用自定义结构作为密钥,则这一点更为关键。

为了从字典中获得良好的性能,GetHashCode()应该是:

  • 快的
  • 能够提供产生很少冲突的哈希代码。唯一实例应在可能的情况下生成唯一的哈希值。
  • 如果你做得对,我想你会非常满意默认的字典实现。


    别忘了,您也在为该代码中的字典构造函数计时。我做了一个测试,将对构造函数的调用移出度量,并循环了10次。这是我的测试代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    for (int i = 0; i < 10; i++)
    {
        Dictionary<string, string> test = new Dictionary<string, string>();

        System.Diagnostics.Stopwatch watch = System.Diagnostics.Stopwatch.StartNew();

        test.Add("fieldName","fieldValue");
        test.Add("Title","fieldavlkajlkdjflkjalkjslkdjfiajwelkrjelrkjavoijl");

        Console.WriteLine(watch.Elapsed);
    }

    Console.ReadKey();

    结果如下:

    2

    我不知道你能跑多快…

    更新

    看起来这也反映了乔恩·斯基茨的成绩……吉特。


    如果您真的需要更好的性能,那么您将不得不放弃一些主要的功能,比如泛型、动态内存分配等。所有这些功能都会牺牲一些性能。

    如果可能的话,我会尽量避免使用contains,并查看TryGetValue等。


    使用ints作为键以获得最佳性能:

    对于任何从谷歌来到这里的人,如果你想从字典中榨取最后一点性能,那么就用ints作为键。下面是比较int和string键的基准:https://jacksondunstan.com/articles/2527

    这篇文章的作者甚至提到,如果您有这样的需求,将字符串转换为整数是值得的。

    另外,请注意,在其他一些语言(如PHP)中也会发生同样的行为。事实上,php关联数组是字典,如果在php7中按升序使用int,它们的性能会大大优于字符串键。


    你计划在字典中添加多少项?尽管Dictionary/Hashtable通常是最快的,但根据您所做的工作,可能有比Hashtable(字典中的底层结构)更快的东西(亦称为更适合)。根据用法,如果与某种跳过列表或甚至自平衡树或尝试组合,排序列表可能更快。尤其是如果您希望返回一个值范围而不是单个值。

    在以下情况下,哈希表非常适合:

  • 您知道在开始填充表之前要存储多少项。动态调整大小会非常痛苦!
  • 你有一个很好的哈希算法,分布均匀,而.NET就是这样做的。
  • 有一个很好的机制可以解决冲突,而.NET可以做到这一点。
  • 你在寻找一个单一的价值
  • 您可以保证所有值都是唯一的
  • 例如,如果要进行一些压缩,RB树比哈希表更好。

    来源:http://en.wikipedia.org/wiki/hashtable动态调整大小


    可以使用一个列表并定义一个枚举,例如fieldname=0,title=1,然后使用每个属性的唯一索引作为列表中的查找索引吗?这将是最快的解决方案,尽管这是自绑定到枚举以来最不灵活的解决方案。


    很可能你找不到比字典快得多的东西。我只会用字典。然后,当你看到你没有达到你的性能目标时,一个分析器指出从字典中添加/删除是你的瓶颈,你可以考虑用一个更有针对性的类来替换。

    请注意,如果不使用linq due等特性,则不会造成任何性能损失。