2014-09-10 55 views
3

我得到了从this post和this一个子字符串struct的想法。第二篇文章是.net的String.GetHashCode()的实现。 (我不确定哪个版本的.net来自于此。)什么导致GetHashCode的这个实现比.net的实现慢20倍?

这是实现。 (GetHashCode的从上面列出的第二源获得。)

public struct Substring 
{ 
    private string String; 
    private int Offset; 
    public int Length { get; private set; } 
    public char this[int index] { get { return String[Offset + index]; } } 

    public Substring(string str, int offset, int len) : this() 
    { 
     String = str; 
     Offset = offset; 
     Length = len; 
    } 

    /// <summary> 
    /// See http://www.dotnetperls.com/gethashcode 
    /// </summary> 
    /// <returns></returns> 
    public unsafe override int GetHashCode() 
    { 
     fixed (char* str = String + Offset) 
     { 
      char* chPtr = str; 
      int num = 352654597; 
      int num2 = num; 
      int* numPtr = (int*)chPtr; 
      for (int i = Length; i > 0; i -= 4) 
      { 
       num = (((num << 5) + num) + (num >> 27))^numPtr[0]; 
       if (i <= 2) 
       { 
        break; 
       } 
       num2 = (((num2 << 5) + num2) + (num2 >> 27))^numPtr[1]; 
       numPtr += 2; 
      } 
      return (num + (num2 * 1566083941)); 
     } 
    } 
} 

这里的一个单元测试:

[Test] 
    public void GetHashCode_IsAsFastAsString() 
    { 
     var s = "The quick brown fox"; 
     var sub = new Substring(s, 1, 5); 
     var t = "quick"; 
     var sum = 0; 

     sum += sub.GetHashCode(); // make sure GetHashCode is jitted 

     var count = 100000000; 
     var sw = Stopwatch.StartNew(); 
     for (var i = 0; i < count; ++i) 
      sum += t.GetHashCode(); 
     var t1 = sw.Elapsed; 
     sw = Stopwatch.StartNew(); 
     for (var i = 0; i < count; ++i) 
      sum += sub.GetHashCode(); 
     var t2 = sw.Elapsed; 

     Debug.WriteLine(sum.ToString()); // make sure we use the return value 
     var m1 = t1.Milliseconds; 
     var m2 = t2.Milliseconds; 
     Assert.IsTrue(m2 <= m1); // fat chance 
    } 

的问题是,m1为10毫秒和m2是190毫秒。 (注意:这是1000000个迭代。)仅供参考,我在.net 4.5 64位版本上运行了这个版本,打开了Optimizations。

+0

与问题无关,但是您是否为了节省内存而编写了这个类? – Matthew 2014-09-10 18:14:18

+3

您正在制造传统的基准标记错误。就像在测量中包含点击开销一样。而不是实际使用返回值,允许抖动优化器完全消除代码。 – 2014-09-10 18:17:26

+0

这是一个很好的观点。所以我回去并在做任何时机之前添加了另一个sub.GetHashCode()循环。同样的结果 - 毫秒。 – bright 2014-09-10 18:22:01

回答

0
  1. 通过评论的线索,我仔细检查,以确保优化的代码运行。事实证明,obscure Debugger setting禁用了优化。所以我没有选中工具 - 选项 - 调试 - 常规 - 在模块加载时禁止JIT优化(仅限管理)。这导致优化的代码正确加载。
  2. 即使开启了优化,仍然存在大约3倍-6倍的差异。但是,这可能是由于上面的代码是.net 32​​位版本,我正在运行64位.net。将string.GetHashCode的64位实现移植到Substring并不容易,因为它依赖于字符串标记的零端(实际上它是bug)。

此时我对没有获得平价表现感到失望,但这是我时间​​学习关于优化C#的一些风险和缺陷的极好用法。