2014-09-10 52 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#的一些風險和缺陷的極好用法。

相關問題