title: What is causing this implementation of GetHashCode to be 20 times slower than .net's implementation?
body: <p>I got the idea of a Substring <code>struct</code> from <a href="https://stackoverflow.com/a/1082495">this</a> post and <a href="http://www.dotnetperls.com/gethashcode" rel="nofollow noreferrer">this</a> one. The second post has the implementation of .net's String.GetHashCode(). (I'm not sure which version of .net this is from.)</p>
  
  <p>Here is the implementation. (GetHashCode is taken from the second source listed above.)</p>
  
  <pre><code>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;
      }
  
      /// &lt;summary&gt;
      /// See http://www.dotnetperls.com/gethashcode
      /// &lt;/summary&gt;
      /// &lt;returns&gt;&lt;/returns&gt;
      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 &gt; 0; i -= 4)
              {
                  num = (((num &lt;&lt; 5) + num) + (num &gt;&gt; 27)) ^ numPtr[0];
                  if (i &lt;= 2)
                  {
                      break;
                  }
                  num2 = (((num2 &lt;&lt; 5) + num2) + (num2 &gt;&gt; 27)) ^ numPtr[1];
                  numPtr += 2;
              }
              return (num + (num2 * 1566083941));
          }
      }
  }
  </code></pre>
  
  <p>Here's a unit test:</p>
  
  <pre><code>    [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 &lt; count; ++i)
              sum += t.GetHashCode();
          var t1 = sw.Elapsed;
          sw = Stopwatch.StartNew();
          for (var i = 0; i &lt; 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 &lt;= m1); // fat chance
      }
  </code></pre>
  
  <p>The problem is that m1 is 10 milliseconds and m2 is 190 milliseconds. (Note: this is with 1000000 iterations.)
  FYI, I ran this on .net 4.5 64 bit Release build with Optimizations turned on.</p>