Comparando dos matrices de bytes en .NET

¿Cómo puedo hacer esto rápido?

Claro que puedo hacer esto:

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; } 

Pero estoy buscando ya sea una función BCL o alguna forma altamente optimizada para hacer esto.

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

funciona bien, pero no parece que eso funcionaría para x64.

Tenga en cuenta mi respuesta súper rápida aquí .

Puede usar el método Enumerable.SequenceEqual .

 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 no puede usar .NET 3.5 por algún motivo, su método es correcto.
Comstackdor \ entorno de tiempo de ejecución optimizará su ciclo para que no tenga que preocuparse por el rendimiento.

P / Invoca poderes activados!

 [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; } 

Hay una nueva solución incorporada para esto en .NET 4 – IStructuralEquatable

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

El usuario gil sugirió un código no seguro que generó esta solución:

 // 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; } } 

que hace una comparación basada en 64 bits para la mayor cantidad posible de la matriz. Este tipo de cuenta cuenta con el hecho de que las matrices comienzan qword alineado. Funcionará si no está alineado con qword, simplemente no tan rápido como si lo fuera.

Realiza aproximadamente siete temporizadores más rápido que el simple for bucle. El uso de la biblioteca J # se realizó de manera equivalente al bucle for original. El uso de .SequenceEqual se ejecuta alrededor de siete veces más lento; Creo que solo porque está usando IEnumerator.MoveNext. Imagino que las soluciones basadas en LINQ son al menos tan lentas o peores.

Si no se opone a hacerlo, puede importar el ensamblado J # “vjslib.dll” y usar su método Arrays.equals (byte [], byte [])

No me culpes si alguien se ríe de ti …


EDITAR: Por lo poco que vale, utilicé Reflector para desensamblar el código para eso, y aquí está lo que parece:

 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; } 

.NET 3.5 y posteriores tienen un nuevo tipo público, System.Data.Linq.Binary que encapsula byte[] . Implementa IEquatable que (en efecto) compara dos matrices de bytes. Tenga en cuenta que System.Data.Linq.Binary también tiene un operador de conversión implícito de byte[] .

Documentación de MSDN: System.Data.Linq.Binary

Descomstackción del reflector del método Equals:

 private bool EqualsTo(Binary binary) { if (this != binary) { if (binary == null) { return false; } if (this.bytes.Length != binary.bytes.Length) { return false; } if (this.hashCode != binary.hashCode) { return false; } int index = 0; int length = this.bytes.Length; while (index < length) { if (this.bytes[index] != binary.bytes[index]) { return false; } index++; } } return true; } 

Un giro interesante es que solo proceden al ciclo de comparación byte a byte si los hashes de los dos objetos binarios son los mismos. Esto, sin embargo, tiene el costo de calcular el hash en el constructor de objetos Binary (atravesando la matriz con for loop :-)).

La implementación anterior significa que en el peor de los casos tendrá que recorrer las matrices tres veces: primero para calcular hash de array1, luego para calcular hash de array2 y finalmente (porque este es el peor escenario posible, longitudes y hashes iguales) para comparar bytes en la matriz 1 con bytes en la matriz 2.

En general, a pesar de que System.Data.Linq.Binary está integrado en BCL, no creo que sea la forma más rápida de comparar matrices de dos bytes: - |.

Publiqué una pregunta similar sobre comprobar si el byte [] está lleno de ceros. (El código SIMD fue derrotado, así que lo eliminé de esta respuesta.) Aquí está el código más rápido de mis comparaciones:

 static unsafe bool EqualBytesLongUnrolled (byte[] data1, byte[] data2) { if (data1 == data2) return true; if (data1.Length != data2.Length) return false; fixed (byte* bytes1 = data1, bytes2 = data2) { int len = data1.Length; int rem = len % (sizeof(long) * 16); long* b1 = (long*)bytes1; long* b2 = (long*)bytes2; long* e1 = (long*)(bytes1 + len - rem); while (b1 < e1) { if (*(b1) != *(b2) || *(b1 + 1) != *(b2 + 1) || *(b1 + 2) != *(b2 + 2) || *(b1 + 3) != *(b2 + 3) || *(b1 + 4) != *(b2 + 4) || *(b1 + 5) != *(b2 + 5) || *(b1 + 6) != *(b2 + 6) || *(b1 + 7) != *(b2 + 7) || *(b1 + 8) != *(b2 + 8) || *(b1 + 9) != *(b2 + 9) || *(b1 + 10) != *(b2 + 10) || *(b1 + 11) != *(b2 + 11) || *(b1 + 12) != *(b2 + 12) || *(b1 + 13) != *(b2 + 13) || *(b1 + 14) != *(b2 + 14) || *(b1 + 15) != *(b2 + 15)) return false; b1 += 16; b2 += 16; } for (int i = 0; i < rem; i++) if (data1 [len - 1 - i] != data2 [len - 1 - i]) return false; return true; } } 

Medido en dos matrices de bytes de 256 MB:

 UnsafeCompare : 86,8784 ms EqualBytesSimd : 71,5125 ms EqualBytesSimdUnrolled : 73,1917 ms EqualBytesLongUnrolled : 39,8623 ms 

Span ofrece una alternativa extremadamente competitiva sin tener que tirar pelusa confusa y / o no portátil en la base de código de su propia aplicación:

 // byte[] is implicitly convertible to ReadOnlySpan static bool ByteArrayCompare(ReadOnlySpan a1, ReadOnlySpan a2) { return a1.SequenceEqual(a2); } 

La implementación (de las entrañas) se puede encontrar aquí .

He revisado la esencia de @ EliArbel para agregar este método como SpansEqual , colocar a la mayoría de los ejecutantes menos interesantes en los puntos de referencia de otros, ejecutarlo con diferentes tamaños de matriz, gráficos de salida y marcar SpansEqual como línea de base para que informe cómo los diferentes métodos compare con SpansEqual .

Los siguientes números son de los resultados, ligeramente editados para eliminar la columna “Error”.

 | Method | ByteCount | Mean | StdDev | Scaled | |-------------- |----------- |-------------------:|---------------:|-------:| | SpansEqual | 15 | 3.614 ns | 0.0069 ns | 1.00 | | LongPointers | 15 | 4.762 ns | 0.0009 ns | 1.32 | | Unrolled | 15 | 16.933 ns | 0.0024 ns | 4.68 | | PInvokeMemcmp | 15 | 11.448 ns | 0.0183 ns | 3.17 | | | | | | | | SpansEqual | 1026 | 25.957 ns | 0.0081 ns | 1.00 | | LongPointers | 1026 | 60.336 ns | 0.0211 ns | 2.32 | | Unrolled | 1026 | 37.216 ns | 0.0042 ns | 1.43 | | PInvokeMemcmp | 1026 | 43.531 ns | 0.0229 ns | 1.68 | | | | | | | | SpansEqual | 1048585 | 42,708.279 ns | 6.7683 ns | 1.00 | | LongPointers | 1048585 | 57,952.010 ns | 6.0004 ns | 1.36 | | Unrolled | 1048585 | 52,768.967 ns | 5.1800 ns | 1.24 | | PInvokeMemcmp | 1048585 | 53,270.846 ns | 11.9056 ns | 1.25 | | | | | | | | SpansEqual | 2147483591 | 243,281,911.498 ns | 65,006.3172 ns | 1.00 | | LongPointers | 2147483591 | 237,786,969.675 ns | 96,332.7202 ns | 0.98 | | Unrolled | 2147483591 | 237,151,053.500 ns | 74,137.6513 ns | 0.97 | | PInvokeMemcmp | 2147483591 | 235,829,644.641 ns | 50,390.2144 ns | 0.97 | 

Me sorprendió ver que SpansEqual no se destacó por los métodos de tamaño máximo de matriz, pero la diferencia es tan pequeña que no creo que vaya a importar.

Mi información del sistema:

 BenchmarkDotNet=v0.10.14, OS=Windows 10.0.17134 Intel Core i7-6850K CPU 3.60GHz (Skylake), 1 CPU, 12 logical and 6 physical cores Frequency=3515619 Hz, Resolution=284.4449 ns, Timer=TSC .NET Core SDK=2.1.300 [Host] : .NET Core 2.1.0 (CoreCLR 4.6.26515.07, CoreFX 4.6.26515.06), 64bit RyuJIT DefaultJob : .NET Core 2.1.0 (CoreCLR 4.6.26515.07, CoreFX 4.6.26515.06), 64bit RyuJIT 
  using System.Linq; //SequenceEqual byte[] ByteArray1 = null; byte[] ByteArray2 = null; ByteArray1 = MyFunct1(); ByteArray2 = MyFunct2(); if (ByteArray1.SequenceEqual(ByteArray2) == true) { MessageBox.Show("Match"); } else { MessageBox.Show("Don't match"); } 

¡Agreguemos uno más!

Recientemente Microsoft lanzó un paquete especial NuGet, System.Runtime.CompilerServices.Unsafe . Es especial porque está escrito en IL y proporciona una funcionalidad de bajo nivel que no está disponible directamente en C #.

Uno de sus métodos, Unsafe.As(object) . Unsafe.As(object) permite Unsafe.As(object) cualquier tipo de referencia a otro tipo de referencia, omitiendo las comprobaciones de seguridad. Esta suele ser una muy mala idea, pero si ambos tipos tienen la misma estructura, puede funcionar. Entonces podemos usar esto para convertir un byte[] en un long[] :

 bool CompareWithUnsafeLibrary(byte[] a1, byte[] a2) { if (a1.Length != a2.Length) return false; var longSize = (int)Math.Floor(a1.Length / 8.0); var long1 = Unsafe.As(a1); var long2 = Unsafe.As(a2); for (var i = 0; i < longSize; i++) { if (long1[i] != long2[i]) return false; } for (var i = longSize * 8; i < a1.Length; i++) { if (a1[i] != a2[i]) return false; } return true; } 

Tenga en cuenta que long1.Length aún devolverá la longitud de la matriz original, ya que se almacena en un campo en la estructura de la memoria de la matriz.

Este método no es tan rápido como otros métodos demostrados aquí, pero es mucho más rápido que el método ingenuo, no utiliza un código inseguro o P / Invoke o pinning, y la implementación es bastante sencilla (IMO). Aquí hay algunos resultados de BenchmarkDotNet de mi máquina:

 BenchmarkDotNet=v0.10.3.0, OS=Microsoft Windows NT 6.2.9200.0 Processor=Intel(R) Core(TM) i7-4870HQ CPU 2.50GHz, ProcessorCount=8 Frequency=2435775 Hz, Resolution=410.5470 ns, Timer=TSC [Host] : Clr 4.0.30319.42000, 64bit RyuJIT-v4.6.1637.0 DefaultJob : Clr 4.0.30319.42000, 64bit RyuJIT-v4.6.1637.0 Method | Mean | StdDev | ----------------------- |-------------- |---------- | UnsafeLibrary | 125.8229 ns | 0.3588 ns | UnsafeCompare | 89.9036 ns | 0.8243 ns | JSharpEquals | 1,432.1717 ns | 1.3161 ns | EqualBytesLongUnrolled | 43.7863 ns | 0.8923 ns | NewMemCmp | 65.4108 ns | 0.2202 ns | ArraysEqual | 910.8372 ns | 2.6082 ns | PInvokeMemcmp | 52.7201 ns | 0.1105 ns | 

También creé una esencia con todas las pruebas .

Usaría un código inseguro y ejecutaría el ciclo for comparar los punteros Int32.

Tal vez también debería considerar verificar que las matrices no sean nulas.

Si observa cómo .NET realiza string.Equals, verá que utiliza un método privado llamado EqualsHelper que tiene una implementación de puntero “inseguro”. .NET Reflector es tu amigo para ver cómo se hacen las cosas internamente.

Esto se puede usar como una plantilla para la comparación de matriz de bytes, en la que realicé una implementación en la comparación de conjuntos de bytes de blog en C # . También hice algunos puntos de referencia rudimentarios para ver cuándo una implementación segura es más rápida que la insegura.

Dicho esto, a menos que realmente necesites un rendimiento despiadado, iría por una simple comparación de bucle fr.

Desarrollé un método que supera ligeramente a memcmp() (respuesta del plinto) y que gana muy levemente EqualBytesLongUnrolled() (respuesta de Arek Bulski). Básicamente, desenrolla el bucle por 4 en lugar de 8.

 public static unsafe bool NewMemCmp(byte* b0, byte* b1, int length) { byte* lastAddr = b0 + length; byte* lastAddrMinus32 = lastAddr - 32; while (b0 < lastAddrMinus32) // unroll the loop so that we are comparing 32 bytes at a time. { if (*(ulong*)b0 != *(ulong*)b1) return false; if (*(ulong*)(b0 + 8) != *(ulong*)(b1 + 8)) return false; if (*(ulong*)(b0 + 16) != *(ulong*)(b1 + 16)) return false; if (*(ulong*)(b0 + 24) != *(ulong*)(b1 + 24)) return false; b0 += 32; b1 += 32; } while (b0 < lastAddr) { if (*b0 != *b1) return false; b0++; b1++; } return true; } public static unsafe bool NewMemCmp(byte[] arr0, byte[] arr1, int length) { fixed (byte* b0 = arr0, b1 = arr1) { return b0 == b1 || NewMemCmp(b0, b1, length); } } 

Esto funciona aproximadamente un 25% más rápido que memcmp() y aproximadamente un 5% más rápido que EqualBytesLongUnrolled() en mi máquina.

Parece que EqualBytesLongUnrolled es el mejor de los sugeridos anteriormente.

Los métodos omitidos (Enumerable.SequenceEqual, StructuralComparisons.StructuralEqualityComparer.Equals) no fueron pacientes-por-lentos. En arreglos de 265MB he medido esto:

 Host Process Environment Information: BenchmarkDotNet.Core=v0.9.9.0 OS=Microsoft Windows NT 6.2.9200.0 Processor=Intel(R) Core(TM) i7-3770 CPU 3.40GHz, ProcessorCount=8 Frequency=3323582 ticks, Resolution=300.8802 ns, Timer=TSC CLR=MS.NET 4.0.30319.42000, Arch=64-bit RELEASE [RyuJIT] GC=Concurrent Workstation JitModules=clrjit-v4.6.1590.0 Type=CompareMemoriesBenchmarks Mode=Throughput Method | Median | StdDev | Scaled | Scaled-SD | ----------------------- |------------ |---------- |------- |---------- | NewMemCopy | 30.0443 ms | 1.1880 ms | 1.00 | 0.00 | EqualBytesLongUnrolled | 29.9917 ms | 0.7480 ms | 0.99 | 0.04 | msvcrt_memcmp | 30.0930 ms | 0.2964 ms | 1.00 | 0.03 | UnsafeCompare | 31.0520 ms | 0.7072 ms | 1.03 | 0.04 | ByteArrayCompare | 212.9980 ms | 2.0776 ms | 7.06 | 0.25 | 

 OS=Windows Processor=?, ProcessorCount=8 Frequency=3323582 ticks, Resolution=300.8802 ns, Timer=TSC CLR=CORE, Arch=64-bit ? [RyuJIT] GC=Concurrent Workstation dotnet cli version: 1.0.0-preview2-003131 Type=CompareMemoriesBenchmarks Mode=Throughput Method | Median | StdDev | Scaled | Scaled-SD | ----------------------- |------------ |---------- |------- |---------- | NewMemCopy | 30.1789 ms | 0.0437 ms | 1.00 | 0.00 | EqualBytesLongUnrolled | 30.1985 ms | 0.1782 ms | 1.00 | 0.01 | msvcrt_memcmp | 30.1084 ms | 0.0660 ms | 1.00 | 0.00 | UnsafeCompare | 31.1845 ms | 0.4051 ms | 1.03 | 0.01 | ByteArrayCompare | 212.0213 ms | 0.1694 ms | 7.03 | 0.01 | 

Para comparar arreglos de bytes cortos, el siguiente es un truco interesante:

 if(myByteArray1.Length != myByteArray2.Length) return false; if(myByteArray1.Length == 8) return BitConverter.ToInt64(myByteArray1, 0) == BitConverter.ToInt64(myByteArray2, 0); else if(myByteArray.Length == 4) return BitConverter.ToInt32(myByteArray2, 0) == BitConverter.ToInt32(myByteArray2, 0); 

Entonces probablemente me caeré a la solución que figura en la pregunta.

Sería interesante hacer un análisis de rendimiento de este código.

No pude encontrar una solución con la que estoy completamente satisfecho (rendimiento razonable, pero no código inseguro / pinvoke) así que se me ocurrió esto, nada realmente original, pero funciona:

  ///  /// ///  ///  ///  ///  0 means compare entire arrays ///  public static bool ArraysEqual(byte[] array1, byte[] array2, int bytesToCompare = 0) { if (array1.Length != array2.Length) return false; var length = (bytesToCompare == 0) ? array1.Length : bytesToCompare; var tailIdx = length - length % sizeof(Int64); //check in 8 byte chunks for (var i = 0; i < tailIdx; i += sizeof(Int64)) { if (BitConverter.ToInt64(array1, i) != BitConverter.ToInt64(array2, i)) return false; } //check the remainder of the array, always shorter than 8 bytes for (var i = tailIdx; i < length; i++) { if (array1[i] != array2[i]) return false; } return true; } 

Rendimiento comparado con algunas de las otras soluciones en esta página:

Simple Loop: 19837 tics, 1.00

* BitConverter: 4886 tics, 4.06

InseguroCompare: 1636 garrapatas, 12.12

EqualBytesLongUnrolled: 637 tics, 31.09

P / Invocar memcmp: 369 tics, 53.67

Probado en linqpad, 1000000 bytes arreglos idénticos (escenario del peor de los casos), 500 iteraciones cada uno.

Pensé en los métodos de aceleración por transferencia de bloques integrados en muchas tarjetas gráficas. Pero entonces tendrías que copiar todos los datos por byte, así que esto no te ayudaría mucho si no quieres implementar una porción completa de tu lógica en código no administrado y dependiente del hardware …

Otra forma de optimización similar al enfoque que se muestra arriba sería almacenar tantos datos como sea posible en un largo [] en lugar de un byte [] desde el inicio, por ejemplo, si lo está leyendo secuencialmente desde un archivo binario, o si usa un archivo mapeado en la memoria, lea los datos como largos [] o valores únicos largos. Entonces, su ciclo de comparación solo necesitará 1/8 de la cantidad de iteraciones que tendría que hacer para un byte [] que contenga la misma cantidad de datos. Se trata de cuándo y con qué frecuencia necesita comparar, cuándo y con qué frecuencia necesita acceder a los datos de forma byte a byte, por ejemplo, usarlo en una llamada API como parámetro en un método que espera un byte []. Al final, solo puedes decir si realmente conoces el caso de uso …

Hice algunas mediciones utilizando la versión de lanzamiento del progtwig .net 4.7 sin el depurador adjunto. Creo que la gente ha estado usando la métrica incorrecta, ya que lo que le interesa si le interesa la velocidad aquí es cuánto tiempo se necesita para descubrir si las matrices de dos bytes son iguales. es decir, rendimiento en bytes.

 StructuralComparison : 2838.8 MiB/s for : 30553811.0 MiB/s ToUInt32 : 23864406.8 MiB/s ToUInt64 : 5526595.7 MiB/s memcmp : 1848977556.1 MiB/s 

Como puede ver, no hay mejor manera que el memcmp y sus órdenes de magnitud son más rápidos. Un bucle for simple es la segunda mejor opción. Y todavía me sorprende por qué Microsoft no puede simplemente incluir un método Buffer.Compare .

[Program.cs]:

 using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; namespace memcmp { class Program { static byte[] TestVector(int size) { var data = new byte[size]; using (var rng = new System.Security.Cryptography.RNGCryptoServiceProvider()) { rng.GetBytes(data); } return data; } static TimeSpan Measure(string testCase, TimeSpan offset, Action action, bool ignore = false) { var t = Stopwatch.StartNew(); var n = 0L; while (t.Elapsed < TimeSpan.FromSeconds(10)) { action(); n++; } var elapsed = t.Elapsed - offset; if (!ignore) { Console.WriteLine($"{testCase,-16} : {n / elapsed.TotalSeconds,16:0.0} MiB/s"); } return elapsed; } [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)] static extern int memcmp(byte[] b1, byte[] b2, long count); static void Main(string[] args) { // how quickly can we establish if two sequences of bytes are equal? // note that we are testing the speed of different comparsion methods var a = TestVector(1024 * 1024); // 1 MiB var b = (byte[])a.Clone(); var offset = Measure("offset", new TimeSpan(), () => { return; }, ignore: true); Measure("StructuralComparison", offset, () => { StructuralComparisons.StructuralEqualityComparer.Equals(a, b); }); Measure("for", offset, () => { for (int i = 0; i < a.Length; i++) { if (a[i] != b[i]) break; } }); Measure("ToUInt32", offset, () => { for (int i = 0; i < a.Length; i += 4) { if (BitConverter.ToUInt32(a, i) != BitConverter.ToUInt32(b, i)) break; } }); Measure("ToUInt64", offset, () => { for (int i = 0; i < a.Length; i += 8) { if (BitConverter.ToUInt64(a, i) != BitConverter.ToUInt64(b, i)) break; } }); Measure("memcmp", offset, () => { memcmp(a, b, a.Length); }); } } } 

Lo siento, si estás buscando una forma administrada, ya lo estás haciendo correctamente y, que sepa, no hay un método integrado en el BCL para hacerlo.

Debe agregar algunas comprobaciones nulas iniciales y luego simplemente reutilizarlas como si estuvieran en BCL.

Es casi seguro que es mucho más lento que cualquier otra versión dada aquí, pero fue divertido escribir.

 static bool ByteArrayEquals(byte[] a1, byte[] a2) { return a1.Zip(a2, (l, r) => l == r).All(x => x); } 

No he visto muchas soluciones linq aquí.

No estoy seguro de las implicaciones de rendimiento, sin embargo, generalmente me linq a linq como regla general y luego linq más adelante si es necesario.

 public bool CompareTwoArrays(byte[] array1, byte[] array2) { return !array1.Where((t, i) => t != array2[i]).Any(); } 

Tenga en cuenta que esto solo funciona si son las mismas matrices de tamaño. una extensión podría verse así

 public bool CompareTwoArrays(byte[] array1, byte[] array2) { if (array1.Length != array2.Length) return false; return !array1.Where((t, i) => t != array2[i]).Any(); } 

Me decidí por una solución inspirada en el método EqualBytesLongUnrolled publicado por ArekBulski con una optimización adicional. En mi caso, las diferencias de matriz en matrices tienden a estar cerca de la cola de las matrices. En las pruebas, descubrí que cuando este es el caso para las matrices grandes, poder comparar los elementos de la matriz en orden inverso le da a esta solución una gran ganancia de rendimiento sobre la solución basada en memcmp. Aquí está esa solución:

 public enum CompareDirection { Forward, Backward } private static unsafe bool UnsafeEquals(byte[] a, byte[] b, CompareDirection direction = CompareDirection.Forward) { // returns when a and b are same array or both null if (a == b) return true; // if either is null or different lengths, can't be equal if (a == null || b == null || a.Length != b.Length) return false; const int UNROLLED = 16; // count of longs 'unrolled' in optimization int size = sizeof(long) * UNROLLED; // 128 bytes (min size for 'unrolled' optimization) int len = a.Length; int n = len / size; // count of full 128 byte segments int r = len % size; // count of remaining 'unoptimized' bytes // pin the arrays and access them via pointers fixed (byte* pb_a = a, pb_b = b) { if (r > 0 && direction == CompareDirection.Backward) { byte* pa = pb_a + len - 1; byte* pb = pb_b + len - 1; byte* phead = pb_a + len - r; while(pa >= phead) { if (*pa != *pb) return false; pa--; pb--; } } if (n > 0) { int nOffset = n * size; if (direction == CompareDirection.Forward) { long* pa = (long*)pb_a; long* pb = (long*)pb_b; long* ptail = (long*)(pb_a + nOffset); while (pa < ptail) { if (*(pa + 0) != *(pb + 0) || *(pa + 1) != *(pb + 1) || *(pa + 2) != *(pb + 2) || *(pa + 3) != *(pb + 3) || *(pa + 4) != *(pb + 4) || *(pa + 5) != *(pb + 5) || *(pa + 6) != *(pb + 6) || *(pa + 7) != *(pb + 7) || *(pa + 8) != *(pb + 8) || *(pa + 9) != *(pb + 9) || *(pa + 10) != *(pb + 10) || *(pa + 11) != *(pb + 11) || *(pa + 12) != *(pb + 12) || *(pa + 13) != *(pb + 13) || *(pa + 14) != *(pb + 14) || *(pa + 15) != *(pb + 15) ) { return false; } pa += UNROLLED; pb += UNROLLED; } } else { long* pa = (long*)(pb_a + nOffset); long* pb = (long*)(pb_b + nOffset); long* phead = (long*)pb_a; while (phead < pa) { if (*(pa - 1) != *(pb - 1) || *(pa - 2) != *(pb - 2) || *(pa - 3) != *(pb - 3) || *(pa - 4) != *(pb - 4) || *(pa - 5) != *(pb - 5) || *(pa - 6) != *(pb - 6) || *(pa - 7) != *(pb - 7) || *(pa - 8) != *(pb - 8) || *(pa - 9) != *(pb - 9) || *(pa - 10) != *(pb - 10) || *(pa - 11) != *(pb - 11) || *(pa - 12) != *(pb - 12) || *(pa - 13) != *(pb - 13) || *(pa - 14) != *(pb - 14) || *(pa - 15) != *(pb - 15) || *(pa - 16) != *(pb - 16) ) { return false; } pa -= UNROLLED; pb -= UNROLLED; } } } if (r > 0 && direction == CompareDirection.Forward) { byte* pa = pb_a + len - r; byte* pb = pb_b + len - r; byte* ptail = pb_a + len; while(pa < ptail) { if (*pa != *pb) return false; pa++; pb++; } } } return true; } 

Use SequenceEquals for this to comparison.

The short answer is this:

  public bool Compare(byte[] b1, byte[] b2) { return Encoding.ASCII.GetString(b1) == Encoding.ASCII.GetString(b2); } 

In such a way you can use the optimized .NET string compare to make a byte array compare without the need to write unsafe code. This is how it is done in the background :

 private unsafe static bool EqualsHelper(String strA, String strB) { Contract.Requires(strA != null); Contract.Requires(strB != null); Contract.Requires(strA.Length == strB.Length); int length = strA.Length; fixed (char* ap = &strA.m_firstChar) fixed (char* bp = &strB.m_firstChar) { char* a = ap; char* b = bp; // Unroll the loop #if AMD64 // For the AMD64 bit platform we unroll by 12 and // check three qwords at a time. This is less code // than the 32 bit case and is shorter // pathlength. while (length >= 12) { if (*(long*)a != *(long*)b) return false; if (*(long*)(a+4) != *(long*)(b+4)) return false; if (*(long*)(a+8) != *(long*)(b+8)) return false; a += 12; b += 12; length -= 12; } #else while (length >= 10) { if (*(int*)a != *(int*)b) return false; if (*(int*)(a+2) != *(int*)(b+2)) return false; if (*(int*)(a+4) != *(int*)(b+4)) return false; if (*(int*)(a+6) != *(int*)(b+6)) return false; if (*(int*)(a+8) != *(int*)(b+8)) return false; a += 10; b += 10; length -= 10; } #endif // This depends on the fact that the String objects are // always zero terminated and that the terminating zero is not included // in the length. For odd string sizes, the last compare will include // the zero terminator. while (length > 0) { if (*(int*)a != *(int*)b) break; a += 2; b += 2; length -= 2; } return (length <= 0); } } 

Since many of the fancy solutions above don’t work with UWP and because I love Linq and functional approaches I pressent you my version to this problem. To escape the comparison when the first difference occures, I chose .FirstOrDefault()

 public static bool CompareByteArrays(byte[] ba0, byte[] ba1) => !(ba0.Length != ba1.Length || Enumerable.Range(1,ba0.Length) .FirstOrDefault(n => ba0[n] != ba1[n]) > 0); 

Kind of brute force, but its straightforward to convert a byte array to a Base64 string and compare the two strings. Handy if you’ve got big arrays to compare. Or if one of the byte arrays are already in Base64 format.

 static bool ByteArrayCompare(byte[] a1, byte[] a2) { string s1 = Convert.ToBase64String(a1); string s2 = Convert.ToBase64String(a2); if(s1 == s2) return true; return false } 

I imagine that the fastest way (with the best performance for large arrays) is to hash both byte arrays and compare the hashes.

If you are looking for a very fast byte array equality comparer, I suggest you take a look at this STSdb Labs article: Byte array equality comparer. It features some of the fastest implementations for byte[] array equality comparing, which are presented, performance tested and summarized.

You can also focus on these implementations:

BigEndianByteArrayComparer – fast byte[] array comparer from left to right (BigEndian) BigEndianByteArrayEqualityComparer – – fast byte[] equality comparer from left to right (BigEndian) LittleEndianByteArrayComparer – fast byte[] array comparer from right to left (LittleEndian) LittleEndianByteArrayEqualityComparer – fast byte[] equality comparer from right to left (LittleEndian)

In case you have a huge byte array, you can compare them by converting them to string.

Puedes usar algo como

 byte[] b1 = // Your array byte[] b2 = // Your array string s1 = Encoding.Default.GetString( b1 ); string s2 = Encoding.Default.GetString( b2 ); 

I have used this and I have seen a huge performance impact.