600 votes

Comparaison de deux tableaux d'octets en .NET

Comment puis-je faire ça rapidement ?

Bien sûr que je peux le faire :

static bool ByteArrayCompare(byte[] a1, byte[] a2)
{
    if (a1.Length != a2.Length)
        return false;

    for (int i=0; i<a1.Length; i++)
        if (a1[i]!=a2[i])
            return false;

    return true;
}

Mais je suis à la recherche soit d'un BCL ou un autre moyen éprouvé et hautement optimisé de le faire.

java.util.Arrays.equals((sbyte[])(Array)a1, (sbyte[])(Array)a2);

fonctionne bien, mais il ne semble pas que cela puisse fonctionner pour x64.

Notez ma réponse ultra-rapide aquí .

1 votes

"Cela compte un peu sur le fait que les tableaux commencent alignés sur qword." C'est un grand si. Vous devriez corriger le code pour refléter cela.

4 votes

Return a1.Length == a2.Length && !a1.Where((t, i) => t != a2[i]).Any() ;

674voto

aku Points 54867

Vous pouvez utiliser Enumerable.SequenceEqual método.

using System;
using System.Linq;
...
var a1 = new int[] { 1, 2, 3};
var a2 = new int[] { 1, 2, 3};
var a3 = new int[] { 1, 2, 4};
var x = a1.SequenceEqual(a2); // true
var y = a1.SequenceEqual(a3); // false

Si vous ne pouvez pas utiliser .NET 3.5 pour une raison quelconque, votre méthode est correcte.
Compilateur \run -L'environnement temps réel optimisera votre boucle afin que vous n'ayez pas à vous soucier des performances.

5 votes

Mais SequenceEqual ne prend-il pas plus de temps à traiter qu'une comparaison non sécurisée ? Surtout lorsque vous effectuez des milliers de comparaisons ?

102 votes

Oui, cela fonctionne environ 50x plus lentement que la comparaison non sécurisée.

1 votes

Merci d'avoir comparé les performances - pour moi, cela rend la décision claire - SequenceEqual est sûr mais lent dans ce cas.

259voto

plinth Points 26817

P/Invocation pouvoirs activés !

[DllImport("msvcrt.dll", CallingConvention=CallingConvention.Cdecl)]
static extern int memcmp(byte[] b1, byte[] b2, long count);

static bool ByteArrayCompare(byte[] b1, byte[] b2)
{
    // Validate buffers are the same length.
    // This also ensures that the count does not exceed the length of either buffer.  
    return b1.Length == b2.Length && memcmp(b1, b2, b1.Length) == 0;
}

51 votes

P/Invoke yaay - cela s'est avéré être le plus rapide de loin sur les bitmaps au moins : stackoverflow.com/questions/2031217/

3 votes

Belle solution, mais si vous ne mettez pas les tableaux d'octets en place, vous risquez d'avoir de gros problèmes.

26 votes

L'épinglage n'est pas nécessaire dans ce cas. Le marshaller effectue un pinning automatique lors de l'appel de code natif avec PInvoke. Référence : stackoverflow.com/questions/2218444/

161voto

Ohad Schneider Points 10485

Il y a une nouvelle solution intégrée pour cela dans .NET 4 - IStructuralEquatable

static bool ByteArrayCompare(byte[] a1, byte[] a2) 
{
    return StructuralComparisons.StructuralEqualityComparer.Equals(a1, a2);
}

21 votes

Según cet article de blog c'est en fait très lent.

59 votes

Follement lent. Environ 180x plus lent qu'une simple boucle for.

1 votes

Pourquoi ne pas simplement StructuralComparisons.StructuralEqualityComparer.Equals(a1, a2) . Non NullReferenceException ici.

84voto

Hafthor Points 5663

Utilisateur gil a suggéré un code dangereux qui a donné naissance à cette solution :

// Copyright (c) 2008-2013 Hafthor Stefansson
// Distributed under the MIT/X11 software license
// Ref: http://www.opensource.org/licenses/mit-license.php.
static unsafe bool UnsafeCompare(byte[] a1, byte[] a2) {
  if(a1==a2) return true;
  if(a1==null || a2==null || a1.Length!=a2.Length)
    return false;
  fixed (byte* p1=a1, p2=a2) {
    byte* x1=p1, x2=p2;
    int l = a1.Length;
    for (int i=0; i < l/8; i++, x1+=8, x2+=8)
      if (*((long*)x1) != *((long*)x2)) return false;
    if ((l & 4)!=0) { if (*((int*)x1)!=*((int*)x2)) return false; x1+=4; x2+=4; }
    if ((l & 2)!=0) { if (*((short*)x1)!=*((short*)x2)) return false; x1+=2; x2+=2; }
    if ((l & 1)!=0) if (*((byte*)x1) != *((byte*)x2)) return false;
    return true;
  }
}

qui effectue une comparaison basée sur 64 bits pour la plus grande partie possible du tableau. Cela compte en quelque sorte sur le fait que les tableaux sont alignés sur qword. Cela fonctionnera s'ils ne sont pas alignés sur qword, mais pas aussi rapidement que s'ils l'étaient.

Il exécute environ sept minuteries plus rapidement que le simple for boucle. L'utilisation de la bibliothèque J# a donné des résultats équivalents à ceux de l'original. for boucle. L'utilisation de .SequenceEqual est environ sept fois plus lente ; je pense que c'est simplement parce qu'elle utilise IEnumerator.MoveNext. J'imagine que les solutions basées sur LINQ sont au moins aussi lentes, voire pires.

3 votes

Bonne solution. Mais une (petite) astuce : Une comparaison si les références a1 et a2 sont égales peut accélérer les choses si on donne le même tableau pour a1 et b1.

0 votes

BTW : J'ai essayé de faire un casting en Guid pour une comparaison 128-bit, mais cela a ralenti le processus.

15 votes

Nouvelles données de test sur la version .NET 4 x64 : IStructualEquatable.equals ~180x plus lent, SequenceEqual 15x plus lent, SHA1 hash compare 11x plus lent, bitconverter ~same, unsafe 7x plus rapide, pinvoke 11x plus rapide. C'est plutôt cool que unsafe soit seulement un peu plus lent que P/Invoke sur memcmp.

31voto

Jason Bunting Points 27534

Si vous n'y êtes pas opposé, vous pouvez importer l'assemblage J# "vjslib.dll" et utiliser sa fonction Méthode Arrays.equals(byte[], byte[]) ...

Ne me blâmez pas si quelqu'un se moque de vous...


EDIT : Pour le peu que ça vaut, j'ai utilisé Reflector pour désassembler le code de cet article, et voici à quoi ça ressemble :

public static bool equals(sbyte[] a1, sbyte[] a2)
{
  if (a1 == a2)
  {
    return true;
  }
  if ((a1 != null) && (a2 != null))
  {
    if (a1.Length != a2.Length)
    {
      return false;
    }
    for (int i = 0; i < a1.Length; i++)
    {
      if (a1[i] != a2[i])
      {
        return false;
      }
    }
    return true;
  }
  return false;
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X