我有一个自定义性能计时器实现。简而言之,它是存储某些代码路径的执行持续时间的静态数据集合。为了识别特定的测量,我需要一组命名对象,可以通过名称快速访问数据项,即中等长度的string
,如20-50个字符。
直截了当的方法可以是Dictionary<string, MyPerformanceCounter>
,可以通过密钥访问,这是计数器ID。
通过List<MyPerformanceCounter>
和List<T>.BinarySearch
排序可以访问和维护的List.Insert
怎么办?当我需要数百个计数器时,它是否有机会获得更多线性性能?
毋庸置疑,我需要尽可能快地访问正确的MyPerformanceCounter
,因为它以每秒几十万的速率调用,并且应该尽可能少地影响代码执行。
新计数器相对较少,每秒一次。
答案 0 :(得分:2)
字典中有几个可能非O(1)的部分。
第一个是生成哈希码。如果您的字符串很长,则每次将其用作字典中的键时,都必须生成字符串的哈希值。字典存储现有密钥的哈希值,因此您不必担心这一点,只需散列您传入的内容。如果字符串都很短,则散列应该很快。与字符串比较相比,长字符串可能需要更长的时间来进行哈希处理。散列会影响读写。
字典的下一个非常量部分是当你有哈希冲突时。它在内部保留了一个具有相同哈希桶的值的链接列表,如果发生哈希冲突,它必须通过并将您的密钥与该桶中的每个项目进行比较。由于你正在使用字符串,并且他们花了很多精力来提供良好的字符串散列函数,这不应该是一个太大的问题。散列冲突会减慢读写速度。
最后一个非常量部分仅在写入期间,如果它耗尽内部存储,则必须在内部重新计算整个哈希表。这仍然比进行数组插入要快得多(就像List&lt;&gt;那样)。如果你只有几百件商品,那绝对不会影响你。
另一方面,列表每个插入平均需要N / 2个副本,每个查找需要log2(N)。除非字符串都有相似的前缀,否则单个比较将比字典快得多,但会有更多的字典。
因此,除非你的字符串很长,以使散列效率低下,否则字典很可能会给你提供更好的性能。
如果您对字符串的性质有所了解,可以编写针对您的方案优化的更具体的数据结构。例如,如果我知道所有字符串都以ASCII大写字母开头,并且每个字符串的长度在5到10个字符之间,那么我可能会创建一个包含26个数组的数组,每个字母对应一个数组,然后每个数组包含6个列表,每个字符串长度一个。像这样:
List<string>[][] lists = new List<string>[26][6];
foreach (string s in keys)
{
var list = lists[s[0] - 'A'][s.Length - 5];
if (list == null)
{
lists[s[0] - 'A'][s.Length] = list = new List<string>();
}
int ix = list.BinarySearch(s);
if (ix < 0)
{
list.Insert(~ix, s);
}
}
如果您有关于您正在处理的数据类型的非常具体的信息,那么这就是您要做的事情。如果你不能做出假设,使用词典很可能是你最好的选择。
如果你想要二进制搜索路由,你可能还想考虑使用OrderedDictionary,我相信它在内部使用二进制搜索树。 https://msdn.microsoft.com/en-us/library/system.collections.specialized.ordereddictionary%28v=vs.110%29.aspx
答案 1 :(得分:0)
我相信你应该使用/path/default/path_1
。
对于小型数据集,列表将具有更好的性能。然而,由于需要更多的元素,词典变得明显优越。
Dictionary<string, MyPerformanceCounter>
所需的时间为: O(1) 常量时间
复杂。 您可以尝试Dictionary
或Hashtable
,但我认为您仍应使用SortedDictionary
。
我在此处提供了基准和指南的链接:http://www.dotnetperls.com/dictionary-time
我希望这会对你有所帮助。