2014-09-10 3 views
3

У меня возникла идея подстроки struct от this и this. Второй пост имеет реализацию .net's String.GetHashCode(). (Я не уверен, какая версия .net это от.)Что вызывает эту реализацию GetHashCode в 20 раз медленнее, чем реализация .net?

Вот реализация. (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 итераций.) FYI, я запустил это на .net 4.5 64-разрядная версия Release build с включенными оптимизациями.

+0

Не связано с проблемой, но вы пишете этот класс в целях экономии памяти? – Matthew

+3

Вы делаете традиционные ошибки маркировки. Как и включение джиттера в измерение. И фактически не использовать возвращаемое значение, позволяя оптимизатору дрожания полностью исключить код. –

+0

Это хороший момент. Поэтому я вернулся и добавил еще один цикл sub.GetHashCode(), прежде чем делать какие-либо сроки. Тот же результат - до миллисекунды. – bright

ответ

0
  1. Подведенный комментарием, я дважды проверял, чтобы оптимизированный код работал. Оказывается, что obscure Debugger setting отключает оптимизацию. Поэтому я не отмечен «Инструменты» - «Параметры» - «Отладка» - «Общие» - запрет оптимизации JIT при загрузке модуля (только для управляемого). Это вызвало оптимизацию кода для правильной загрузки.
  2. Даже при включенной оптимизации все еще существует разница в 3x - 6x. Однако это может быть связано с тем, что приведенный выше код представляет собой 32-разрядную версию .net, и я запускаю 64-разрядную сеть .net. Портирование 64-битной реализации string.GetHashCode в подстроку не так просто, потому что оно опирается на нулевой конец строкового маркера (который фактически является bug).

В настоящее время я разочарован тем, что не получаю производительность паритета, но это было отличное использование моего времени в изучении некоторых опасностей и ловушек оптимизации C#.

 Смежные вопросы

  • Нет связанных вопросов^_^