2012-02-01 3 views
3

Я читал this question и another question. В одном из этих двух вопросов я также читал, что структура Guid состоит из следующих четырех полей: Int32, Int16, Int16 и Byte [8], поэтому сравнение между двумя Guid должно быть быстрее.Производительность в сравнении GUID

Ну, я использую структуру Guid только для генерации UUID, а затем мне нужно сравнить только те, сгенерированные ранее UUID. Поэтому я хотел бы конвертировать каждый UUID, сгенерированный быстро в формате, сопоставимом.

Я провел несколько тестов, используя следующий код (я получил вдохновение от this question).

 struct FastUuid 
     { 
      public long _part1; 
      public long _part2; 

      public FastUuid(byte[] value) 
      { 
       _part1 = BitConverter.ToInt64(value, 0); 
       _part2 = BitConverter.ToInt64(value, 8); 
      } 
     } 

     static void Main(string[] args) 
     { 
      TestUuidCompare(1000000000); 
      Console.ReadLine(); 

     } 

     public static void TestUuidCompare(uint numberOfChecks) 
     { 
      Guid uuid1 = Guid.NewGuid(); 
      Guid uuid2 = new Guid(uuid1.ToByteArray()); 

      byte[] a1 = uuid1.ToByteArray(); 
      byte[] a2 = uuid2.ToByteArray(); 

      string s1 = uuid1.ToString(); 
      string s2 = uuid2.ToString(); 

      BigInteger b1 = new BigInteger(uuid1.ToByteArray()); 
      BigInteger b2 = new BigInteger(uuid2.ToByteArray()); 

      long l1part1 = BitConverter.ToInt64(uuid1.ToByteArray(), 0); // Parts 1 and 2 
      long l1part2 = BitConverter.ToInt64(uuid1.ToByteArray(), 8); // of uuid1. 

      long l2part1 = BitConverter.ToInt64(uuid2.ToByteArray(), 0); // Parts 1 and 2 
      long l2part2 = BitConverter.ToInt64(uuid2.ToByteArray(), 8); // of uuid2. 

      long[] la1 = { l1part1, l1part2 }; // Parts 1 and 2 of uuid1. 
      long[] la2 = { l2part1, l2part2 }; // Parts 1 and 2 of uuid2. 

      int i1part1 = BitConverter.ToInt32(uuid1.ToByteArray(), 0); // Parts 1, 2, 3 
      int i1part2 = BitConverter.ToInt32(uuid1.ToByteArray(), 4); // and 4 
      int i1part3 = BitConverter.ToInt32(uuid1.ToByteArray(), 8); // of 
      int i1part4 = BitConverter.ToInt32(uuid1.ToByteArray(), 12); // uuid1. 

      int i2part1 = BitConverter.ToInt32(uuid2.ToByteArray(), 0); // Parts 1, 2, 3 
      int i2part2 = BitConverter.ToInt32(uuid2.ToByteArray(), 4); // and 4 
      int i2part3 = BitConverter.ToInt32(uuid2.ToByteArray(), 8); // of 
      int i2part4 = BitConverter.ToInt32(uuid2.ToByteArray(), 12); // uuid2 

      FastUuid fast1 = new FastUuid(uuid1.ToByteArray()); 
      FastUuid fast2 = new FastUuid(uuid2.ToByteArray()); 


      // UUID are equal (worse scenario) 

      Stopwatch sw = new Stopwatch(); 
      bool result; 

      sw.Reset(); sw.Start(); 
      for (int i = 0; i < numberOfChecks; i++) 
      { 
       result = (uuid1 == uuid2); 
      } 
      Console.WriteLine("- Guid.Equals: \t{0}", sw.Elapsed); 

      sw.Reset(); sw.Start(); 
      for (int i = 0; i < numberOfChecks; i++) 
      { 
       result = Array.Equals(a1, a2); 
      } 
      Console.WriteLine("- Array.Equals(byte): \t{0}", sw.Elapsed); 

      sw.Reset(); sw.Start(); 
      for (int i = 0; i < numberOfChecks; i++) 
      { 
       result = s1.Equals(s2); 
      } 
      Console.WriteLine("- String.Equals: \t{0}", sw.Elapsed); 

      sw.Reset(); sw.Start(); 
      for (int i = 0; i < numberOfChecks; i++) 
      { 
       result = b1.Equals(b2); 
      } 
      Console.WriteLine("- BigInteger.Equals: \t{0}", sw.Elapsed); 

      sw.Reset(); sw.Start(); 
      for (int i = 0; i < numberOfChecks; i++) 
      { 
       result = (l1part1 == l2part1 && l1part2 == l2part2); 
      } 
      Console.WriteLine("- Two long compare: \t{0}", sw.Elapsed); 

      sw.Reset(); sw.Start(); 
      for (int i = 0; i < numberOfChecks; i++) 
      { 
       result = Array.Equals(la1, la2); 
      } 
      Console.WriteLine("- Array.Equals(long): \t{0}", sw.Elapsed); 

      sw.Reset(); sw.Start(); 
      for (int i = 0; i < numberOfChecks; i++) 
      { 
       result = (i1part1 == i2part1 && i1part2 == i2part2 && i1part3 == i2part3 && i1part4 == i2part4); 
      } 
      Console.WriteLine("- Four int compare: \t{0}", sw.Elapsed); 

      sw.Reset(); sw.Start(); 
      for (int i = 0; i < numberOfChecks; i++) 
      { 
       result = fast1.Equals(fast2); 
      } 
      Console.WriteLine("- FastUuid: \t{0}", sw.Elapsed); 
     } 

Со следующими результатами.

  • Guid.Equals: 18.911 сек
  • Array.Equals (байт): 12.003 сек
  • String.equals: 26.159 сек
  • BigInteger.Equals: 22,652 с
  • Два длинных сравнения: 6,530 s < --- самый быстрый
  • Array.Equals (длинный): 11,930 s
  • Четыре ИНТ сравнения: 6,795 s
  • FastUuid: 1m 26.636 s < --- самый медленный

Почему сравнение FastUuid является самым медленным? Поскольку UUID должен быть ключом к Dictionary, есть ли способ получить сравнение производительности между двумя длинными и в то же время реализовать UUID с небольшим (16-байтным) объектом/структурой?

EDIT: На самом деле, тесты, которые я выполнял измерения производительности сравнения между двумя UUID, поэтому они имеют мало смысла, чтобы оценить производительность доступа к словарю, потому что, когда я призываю ContainsKey method, он вычисляет хэш-значение UUID. Должен ли я оценивать производительность при вычислении хэш-значения Guid, String, FastUuid и т. Д.? Как работает метод ContainsKey?

+2

Для человека, который downvoted на вопрос OP, то почему? Конструктивная обратная связь всегда приветствуется! – Will

+7

Ваш тест весьма спорный. Вы проверяете крайне маловероятный случай наличия 1000000000 равных указателей. Guid.Equals() оптимизирован для гораздо более вероятного случая, когда они * не будут * равны. –

+0

Я установил 1000000000 проверок, чтобы получить лучшее сравнение. – enzom83

ответ

5

По умолчанию для Equals для структур используется отражение для сравнения, которое медленнее. (См. Больше на ValueType.Equals на MSDN.)

Вы должны переопределить Equals и обеспечить собственную реализацию:

public override bool Equals(object other) 
{ 
    var fastOther = other as FastUuid?; 
    return fastOther.HasValue && 
     fastOther.Value._part1 == _part1 && 
     fastOther.Value._part2 == _part2; 
} 

Это не решит проблему полностью, однако. Поскольку это структура, вы должны также осуществлять IEquatable<FastUuid>, чтобы избежать необходимости в поле/распаковывать другой пункт:

public bool Equals(FastUuid other) 
{ 
    return 
     other._part1 == _part1 && 
     other._part2 == _part2; 
} 

Равно затем просто:

public override bool Equals(object other) 
{ 
    var fastOther = other as FastUuid?; 
    return fastOther.HasValue && Equals(fastOther.Value); 
} 
+0

Используя это решение, прошедшее время увеличивается: 3m 52 s – enzom83

+2

Извините, я также должен иметь показано, как избежать бокса в другой структуре. Я обновил этот пример. – porges

+1

Отлично! Около 24 с! – enzom83

3

Структуры используют отражение во время выполнения, чтобы определить, что нужно сравнивать. Переопределите Equals с помощью собственного метода сравнения, чтобы получить гораздо более быстрые результаты.

см http://msdn.microsoft.com/en-us/library/2dts52z7.aspx

Update - Перекрытие равно для структуры заканчивается имея бокс накладных расходов (преобразование-структуру объекта и обратно структуру), но если вы определяете Equals, который принимает FastUuid структуры непосредственно, он работает гораздо быстрее :

 public bool Equals(FastUuid value) 
     { 
      return this._part1 == value._part1 
       && this._part2 == value._part2; 
     } 

Это работает немного быстрее, чем реализация Guid на моей коробке (4.2sec против 5.8sec, но все же медленнее, чем длинные сравнения в 1.7sec).

пс. Pls будьте осторожны, чтобы запускать тесты только с версиями релизов, так как отладочные сборки могут серьезно повлиять на сроки.

+0

Хорошо! В режиме выпуска эти времена уменьшаются: - Два длинных сравнения: 2.291 s - Four int compare: 2.245 s - FastUuid: 10.018 s – enzom83

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