ry中使用IEqualityComparer的效率与HashCo

ry中使用IEqualityComparer的效率与HashCo

本文介绍了在Dictionary中使用IEqualityComparer的效率与HashCode和Equals()的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



我想知道使用 IEqualityComparer 字典< K,V> 当提供一个时,这一切如何工作?



谢谢


解决方案

是否更快?



从gamedev的角度来看,如果您的密钥是提供您自己的 EqualityComparer< T> 的值类型(struct,primitive,enum等),明显更快 - 由于事实



作为一个现实世界的例子,托管的DirectX广告牌($ {code> EqualityComparer< T> .Default 样本用于运行在C ++版本速度的30%左右;其他所有样品运行在〜90%。这样做的原因是广告牌正在使用默认的比较器(因此被装箱)进行排序,因为这样就可以在每个帧的周围复制4MB的数据。



它如何工作



字典< K,V> 将通过默认构造函数向其自身提供 EqualityComparer< T> .Default 。默认的等效比较器是什么(基本上,注意拳击发生的程度):

  public void GetHashCode(T value)
{
return((object)value).GetHashCode();


public void等于(T first,T second)
{
return((object)first).Equals((object)second);
}

为什么我会使用它? p>

看到这种代码(尝试使用不区分大小写的键)时很常见:

  var dict = new Dictionary< string,int>(); 
dict.Add(myParam.ToUpperInvariant(),fooParam);
// ...
var val = dict [myParam.ToUpperInvariant()]

这真的很浪费,最好只是在构造函数上使用StringComparer:

  var dict = new Dictionary< string,int>(StringComparer.OrdinalIgnoreCase); 

更快(redux)吗?



在这个具体的场景中,这是一个更快的速度,因为顺序字符串比较是您可以做的最快的字符串比较类型。一个快速测试:

  static void Main(string [] args)
{
var d1 = new字典< string,int>();
var d2 = new Dictionary< string,int>(StringComparer.OrdinalIgnoreCase);

d1.Add(FOO,1);
d2.Add(FOO,1);

秒表s =新秒表();
s.Start();
RunTest1(d1,foo);
s.Stop();
Console.WriteLine(ToUpperInvariant:{0},s.El​​apsed);

s.Reset();
s.Start()
RunTest2(d2,foo);
s.Stop();
Console.WriteLine(OrdinalIgnoreCase:{0},s.El​​apsed);

Console.ReadLine();


static void RunTest1(Dictionary< string,int> values,string val)
{
for(var i = 0; i< 10000000; i ++ )
{
values [val.ToUpperInvariant()] = values [val.ToUpperInvariant()];



static void RunTest2(Dictionary< string,int> values,string val)
{
for(var i = 0; i < 10000000; i ++)
{
values [val] = values [val];
}
}

// ToUpperInvariant:00:00:04.5084119
// OrdinalIgnoreCase:00:00:02.1211549
// 2x更快。

预订



IEquatable< T> )来消除拳击开销。然而,在这种情况下出现拳击时,有许多令人惊奇的规则,所以我建议在所有可能的情况下,使用配对的界面(例如 IEqualityComparer< T> )。


The title is pretty much clear I think.

I was wondering if there's a certain efficiency overhead when using IEqualityComparer in a Dictionary<K,V> how does it all work when providing one?

Thanks

解决方案

Is it Faster?

Coming from a gamedev perspective, if your key is a value type (struct, primitive, enum, etc.) providing your own EqualityComparer<T> is significantly faster - due to the fact the EqualityComparer<T>.Default boxes the value.

As a real-world example, the Managed DirectX billboard sample used to run at ~30% of the speed of the C++ version; where all the other samples were running at ~90%. The reason for this was that the billboards were being sorted using the default comparer (and thus being boxed), as it turns out 4MB of data was being copied around every frame thanks to this.

How does it work?

Dictionary<K,V> will provide EqualityComparer<T>.Default to itself via the default constructor. What the default equality comparer does is (basically, notice how much boxing occurs):

public void GetHashCode(T value)
{
   return ((object)value).GetHashCode();
}

public void Equals(T first, T second)
{
   return ((object)first).Equals((object)second);
}

Why would I ever use it?

It's quite common to see this kind of code (when trying to have case-insensitive keys):

var dict = new Dictionary<string, int>();
dict.Add(myParam.ToUpperInvariant(), fooParam);
// ...
var val = dict[myParam.ToUpperInvariant()];

This is really wasteful, it is better to just use a StringComparer on the constructor:

var dict = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);

Is it faster (redux)?

In this specific scenario it is a lot faster, because ordinal string comparisons are the fastest type of string comparison you can do. A quick benchmark:

static void Main(string[] args)
{
    var d1 = new Dictionary<string, int>();
    var d2 = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);

    d1.Add("FOO", 1);
    d2.Add("FOO", 1);

    Stopwatch s = new Stopwatch();
    s.Start();
    RunTest1(d1, "foo");
    s.Stop();
    Console.WriteLine("ToUpperInvariant: {0}", s.Elapsed);

    s.Reset();
    s.Start();
    RunTest2(d2, "foo");
    s.Stop();
    Console.WriteLine("OrdinalIgnoreCase: {0}", s.Elapsed);

    Console.ReadLine();
}

static void RunTest1(Dictionary<string, int> values, string val)
{
    for (var i = 0; i < 10000000; i++)
    {
        values[val.ToUpperInvariant()] = values[val.ToUpperInvariant()];
    }
}

static void RunTest2(Dictionary<string, int> values, string val)
{
    for (var i = 0; i < 10000000; i++)
    {
        values[val] = values[val];
    }
}

// ToUpperInvariant: 00:00:04.5084119
// OrdinalIgnoreCase: 00:00:02.1211549
// 2x faster.

Reservations

It is possible to eliminate the boxing overhead by implementing an interface on a struct (such as IEquatable<T>). However, there are many surprising rules for when boxing occurs under these circumstances so I would recommend using the paired interface (e.g. IEqualityComparer<T> in this case) if at all possible.

这篇关于在Dictionary中使用IEqualityComparer的效率与HashCode和Equals()的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-06 22:31