c# .net arrays performance j#

c# - Comparando dos matrices de bytes en.NET



arrays performance (28)

¡Vamos a añadir uno más!

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

Uno de sus métodos, Unsafe.As<T>(object) . Unsafe.As<T>(object) permite Unsafe.As<T>(object) cualquier tipo de referencia a otro tipo de referencia, omitiendo cualquier verificación de seguridad. Esta suele ser una muy mala idea, pero si ambos tipos tienen la misma estructura, puede funcionar. Así que podemos usar esto para convertir un byte[] a 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<long[]>(a1); var long2 = Unsafe.As<long[]>(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 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 usa 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 he creado una esencia con todas las pruebas .

¿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 una función BCL o alguna forma probada altamente optimizada para hacer esto.

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

funciona bien, pero no parece que funcione para x64.

Note mi respuesta súper rápida here .


.NET 3.5 y posteriores tienen un nuevo tipo público, System.Data.Linq.Binary que encapsula el byte[] . Implementa IEquatable<Binary> que (en efecto) compara matrices de dos 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

Reflector de descompilación 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 bucle de comparación byte a byte si los hashes de los dos objetos binarios son iguales. Sin embargo, esto conlleva 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 es posible que tenga que atravesar los arreglos tres veces: primero para calcular el hash de array1, luego para calcular el hash de array2 y finalmente (porque este es el peor escenario, longitudes y hashes iguales) para comparar bytes en array1 con bytes en array 2.

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


Desarrollé un método que supera ligeramente a memcmp() (respuesta del pedestal) y supera EqualBytesLongUnrolled() (respuesta de Arek Bulski). Básicamente, desenrolla el bucle en 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 se ejecuta aproximadamente un 25% más rápido que memcmp() y aproximadamente un 5% más rápido que EqualBytesLongUnrolled() en mi máquina.


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

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

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


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

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


Hay una nueva solución integrada para esto en .NET 4 - IStructuralEquatable

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


Hice algunas mediciones utilizando el programa adjunto .net 4.7 versión de compilación sin el depurador adjunto. Creo que la gente ha estado usando la métrica incorrecta, ya que de lo que habla si le importa la velocidad aquí es cuánto tiempo se tarda en averiguar si las matrices de dos bytes son iguales. Es decir, el 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 memcmp y es más rápido en órdenes de magnitud. Un simple for loop es la segunda mejor opción. Y aún 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á buscando una forma administrada ya lo está haciendo correctamente y, que yo sepa, no hay un método integrado en el BCL para hacer esto.

Debe agregar algunas verificaciones nulas iniciales y luego reutilizarlo como si estuviera en BCL.


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 matrices en las matrices tienden a estar cerca de la cola de las matrices. En las pruebas, descubrí que cuando este es el caso de los arreglos grandes, el hecho de poder comparar los elementos del arreglo en orden inverso le da a esta solución una gran ganancia de rendimiento con respecto a 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; }


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 tarde 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 matrices del mismo 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(); }


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

/// <summary> /// /// </summary> /// <param name="array1"></param> /// <param name="array2"></param> /// <param name="bytesToCompare"> 0 means compare entire arrays</param> /// <returns></returns> 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:

Bucle simple: 19837 garrapatas, 1.00

* BitConverter: 4886 ticks, 4.06

UnsafeCompare: 1636 ticks, 12.12

EqualBytesLongUnrolled: 637 ticks, 31.09

P / Invoke memcmp: 369 ticks, 53.67

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


Para comparar arrays de bytes cortos, lo siguiente es un hack 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 quedaría con la solución enumerada en la pregunta.

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


Parece que EqualBytesLongUnrolled es el mejor de los sugeridos anteriormente.

Los métodos omitidos (Enumerable.SequenceEqual, StructuralComparisons.StructuralEqualityComparer.Equals), no fueron para el paciente para lento. En matrices 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 |


Pensé en los métodos de aceleración de transferencia de bloques integrados en muchas tarjetas gráficas. Pero entonces tendría que copiar todos los datos por bytes, por lo que esto no le ayudará mucho si no desea implementar una parte completa de su lógica en código no administrado y dependiente del hardware ...

Otra forma de optimización similar al enfoque mostrado anteriormente sería almacenar la mayor cantidad posible de sus datos en un largo [] en lugar de en un byte [] desde el principio, por ejemplo, si lo está leyendo secuencialmente desde un archivo binario. o si usa un archivo mapeado en memoria, lea los datos con una longitud larga de [] o valores únicos. Entonces, su ciclo de comparación solo necesitará 1/8 del número de iteraciones que tendría que hacer para un byte [] que contenga la misma cantidad de datos. Es una cuestión de cuándo y con qué frecuencia necesita comparar en comparación con cuándo y con qué frecuencia necesita acceder a los datos en una forma de byte a byte, por ejemplo, para usarlos en una llamada a la API como un parámetro en un método que espera un byte []. Al final, solo puedes decir si realmente sabes el caso de uso ...


Puede utilizar 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 alguna razón, su método es correcto.
El entorno de compilación / tiempo de ejecución optimizará su bucle, por lo que no necesita preocuparse por el rendimiento.


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 aunque ...

EDITAR: Por lo poco que vale, utilicé Reflector para desensamblar el código para eso, y esto es 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; }


Si observa cómo .NET realiza string.Equals, verá que utiliza un método privado llamado EqualsHelper que tiene una implementación de puntero "no segura". .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 matrices de bytes en la que hice una implementación en la publicación del blog. Comparación rápida de matrices de bytes 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 espectacular, buscaría una comparación simple de bucles fr.


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

Tal vez debería considerar también que la verificación de las matrices no sea nula.


Publiqué una pregunta similar sobre la verificación de si el byte [] está lleno de ceros. (El código SIMD fue golpeado, 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 256 MB de bytes:

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


P/Invoke poderes activar!

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


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

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

La (agallas de la) implementación se puede encontrar here .

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

Los números a continuación corresponden a 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 está en la cima de los métodos de tamaño máximo de matriz, pero la diferencia es tan pequeña que no creo que importe nunca.

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


En caso de que tenga una gran matriz de bytes, puede compararlos convirtiéndolos en cadena.

Puedes usar algo como

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

He usado esto y he visto un gran impacto en el rendimiento.


La respuesta corta es esta:

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

De esta manera, puede utilizar la comparación optimizada de cadenas .NET para hacer una comparación de matrices de bytes sin la necesidad de escribir código no seguro. Así es como se hace en el 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); } }


Dado que muchas de las soluciones sofisticadas anteriores no funcionan con UWP y porque me encantan Linq y los enfoques funcionales, les presento mi versión a este problema. Para escapar de la comparación cuando ocurre la primera diferencia, elegí .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);


Si está buscando un comparador de igualdad de byte muy rápido, le sugiero que eche un vistazo a este artículo de STSdb ​​Labs: Comparador de igualdad de matriz de byte. Presenta algunas de las implementaciones más rápidas para la comparación de la igualdad de bytes [], que se presentan, se prueban y se resumen en el rendimiento.

También puedes enfocarte en estas implementaciones:

BigEndianByteArrayComparer - octeto rápido [] array comparador de izquierda a derecha (BigEndian) BigEndianByteArrayComparer - - byte rápido [] comparador de igualdad de izquierda a derecha (BigEndian) LittleEndianByteArrayComparer - octeto rápido [] array comparador de derecha a izquierda (LittleEndian) LittleEndianByteArrayEqualityComparer - octeto rápido [] comparador de igualdad de derecha a izquierda (LittleEndian)


Tipo de fuerza bruta, pero es sencillo convertir una matriz de bytes a una cadena Base64 y comparar las dos cadenas. Práctico si tienes grandes arreglos para comparar. O si una de las matrices de bytes ya está en formato Base64.

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

Me imagino que la forma más rápida (con el mejor rendimiento para arreglos grandes) es agrupar ambos arreglos de bytes y comparar los hashes.


Utilice SequenceEqualspara esto a la comparación.


using System.Linq; //SequenceEqual byte[] ByteArray1 = null; byte[] ByteArray2 = null; ByteArray1 = MyFunct1(); ByteArray2 = MyFunct2(); if (ByteArray1.SequenceEqual<byte>(ByteArray2) == true) { MessageBox.Show("Match"); } else { MessageBox.Show("Don''t match"); }