¿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.
Tenga en cuenta mi respuesta súper rápida aquí .
IStructuralEquatable
Respuestas:
Puede usar el método Enumerable.SequenceEqual .
Si no puede usar .NET 3.5 por alguna razón, su método está bien.
El entorno de compilador \ tiempo de ejecución optimizará su ciclo para que no tenga que preocuparse por el rendimiento.
fuente
P / Invocar poderes activados!
fuente
Hay una nueva solución integrada para esto en .NET 4: IStructuralEquatable
fuente
StructuralComparisons.StructuralEqualityComparer.Equals(a1, a2)
? No hayNullReferenceException
s aquí.El usuario gil sugirió un código inseguro que generó esta solución:
que hace una comparación basada en 64 bits para la mayor cantidad posible de la matriz. Este tipo de cuenta con el hecho de que las matrices comienzan qword alineado. Funcionará si no qword alineado, solo que no tan rápido como si lo fuera.
Realiza aproximadamente siete temporizadores más rápido que el
for
bucle simple . El uso de la biblioteca J # se realizó de manera equivalente alfor
bucle 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.fuente
Span<T>
ofrece una alternativa extremadamente competitiva sin tener que tirar pelusas confusas y / o no portátiles en la base de código de su propia aplicación:La implementación (de las entrañas de) de .NET Core 3.1.0 se puede encontrar aquí .
He revisé @ quid de EliArbel añadir este método
SpansEqual
, la caída de la mayoría de los artistas menos interesantes en los puntos de referencia de los demás, funciona con diferentes tamaños de matriz, gráficos de salida, y la marcaSpansEqual
como línea de base para que se informa de cómo los diferentes métodos se comparan conSpansEqual
.Los siguientes números son de los resultados, ligeramente editados para eliminar la columna "Error".
Me sorprendió ver que
SpansEqual
no estaba 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.Mi información del sistema:
fuente
{ReadOnly,}Span<T>
tiene su propia versión deSequenceEqual
(mismo nombre porque tiene el mismo contrato que elIEnumerable<T>
método de extensión correspondiente , es más rápido). Tenga en cuenta que{ReadOnly,}Span<T>
no puede usarIEnumerable<T>
métodos de extensión debido a las restricciones en losref struct
tipos.Span<T>
implementaciones "portátiles" / "lentas" paranetstandard1.1
y superiores (así que juega con este gráfico interactivo para ver cuáles son). "Rápido"Span<T>
solo está disponible en .NET Core 2.1, en este momento, pero tenga en cuenta queSequenceEqual<T>
, específicamente, debería haber muy poca diferencia entre "rápido" y "lento" / "portátil" (aunque losnetstandard2.0
objetivos deberían ver una ligera mejora porque tener la ruta del código vectorizado).Si no se opone a hacerlo, puede importar el ensamblado J # "vjslib.dll" y usar su método Arrays.equals (byte [], byte []) ...
Aunque no me culpes si alguien se ríe de ti ...
EDITAR: por lo poco que vale, utilicé Reflector para desmontar el código para eso, y así es como se ve:
fuente
.NET 3.5 y posteriores tienen un nuevo tipo público,
System.Data.Linq.Binary
que encapsulabyte[]
. ImplementaIEquatable<Binary>
que (en efecto) compara dos conjuntos de bytes. Tenga en cuenta queSystem.Data.Linq.Binary
también tiene un operador de conversión implícito debyte[]
.Documentación de MSDN: System.Data.Linq.Binary
Reflector de descompilación del método Equals:
Un giro interesante es que solo proceden al bucle de comparación byte por byte si los hashes de los dos objetos binarios son iguales. Sin embargo, esto tiene el costo de calcular el hash en el constructor de
Binary
objetos (atravesando la matriz confor
loop :-)).La implementación anterior significa que, en el peor de los casos, es posible que tenga que atravesar las matrices tres veces: primero para calcular el hash de array1, luego para calcular el hash de array2 y finalmente (porque este es el peor de los casos, longitudes y hashes iguales) para comparar bytes en matriz1 con bytes en matriz 2.
En general, aunque
System.Data.Linq.Binary
está integrado en BCL, no creo que sea la forma más rápida de comparar dos conjuntos de bytes: - |.fuente
Publiqué una pregunta similar sobre cómo verificar si el byte [] está lleno de ceros. (El código SIMD fue eliminado, así que lo eliminé de esta respuesta). Aquí está el código más rápido de mis comparaciones:
Medido en dos conjuntos de bytes de 256 MB:
fuente
fuente
¡Agreguemos uno más!
Recientemente, Microsoft lanzó un paquete especial de 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<T>(object)
permite convertir 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. Entonces podemos usar esto para lanzar unbyte[]
a along[]
:Tenga en cuenta que
long1.Length
aún devolvería la longitud de la matriz original, ya que está almacenada 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). Estos son algunos resultados de BenchmarkDotNet de mi máquina:
También he creado una esencia con todas las pruebas .
fuente
NewMemCmp
respuesta para usar AVX-2Desarrollé un método que late ligeramente
memcmp()
(la respuesta del zócalo) y muy ligeramenteEqualBytesLongUnrolled()
(la respuesta de Arek Bulski) en mi PC. Básicamente, desenrolla el bucle por 4 en lugar de 8.Actualización 30 de marzo de 2019 :
A partir de .NET core 3.0, ¡tenemos soporte SIMD!
Esta solución es más rápida por un margen considerable en mi PC:
fuente
null
.Span
ymemcpy
?Usaría código inseguro y ejecutaría el
for
bucle comparando punteros Int32.Quizás también debería considerar verificar que las matrices no sean nulas.
fuente
Si observa cómo .NET hace string.Equals, verá que usa un método privado llamado EqualsHelper que tiene una implementación de puntero "insegura". .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 la matriz de bytes en la que hice una implementación en la publicación del blog Comparación rápida de la matriz 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 excelente, elegiría una simple comparación de bucles fr.
fuente
No pude encontrar una solución con la que estoy completamente satisfecho (rendimiento razonable, pero sin código / pinvoke inseguro), así que se me ocurrió esto, nada realmente original, pero funciona:
Rendimiento comparado con algunas de las otras soluciones en esta página:
Bucle simple: 19837 ticks, 1.00
* BitConverter: 4886 ticks, 4.06
Comparación insegura: 1636 ticks, 12.12
EqualBytesLongUnrolled: 637 ticks, 31.09
P / invocar memcmp: 369 ticks, 53.67
Probado en linqpad, matrices idénticas de 1000000 bytes (peor de los casos), 500 iteraciones cada una.
fuente
Parece que EqualBytesLongUnrolled es el mejor de lo sugerido anteriormente.
Los métodos omitidos (Enumerable.SequenceEqual, StructuralComparisons.StructuralEqualityComparer.Equals) no fueron pacientes por lento. En matrices de 265MB he medido esto:
fuente
NewMemCmp
respuesta para usar AVX-2No he visto muchas soluciones linq aquí.
No estoy seguro de las implicaciones de rendimiento, sin embargo, generalmente me mantengo
linq
como regla general y luego optimizo más tarde si es necesario.Tenga en cuenta que esto solo funciona si son matrices del mismo tamaño. una extensión podría verse así
fuente
Hice algunas mediciones usando 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 lo que le importa si le importa la velocidad aquí es cuánto tiempo lleva averiguar si las matrices de dos bytes son iguales. es decir, rendimiento en bytes.
Como puede ver, no hay mejor manera
memcmp
y sus órdenes de magnitud son más rápidas. Unfor
bucle simple es la segunda mejor opción. Y todavía me sorprende por qué Microsoft no puede simplemente incluir unBuffer.Compare
método.[Program.cs]:
fuente
Para comparar matrices de bytes cortos, lo siguiente es un truco interesante:
Entonces probablemente caería en la solución que figura en la pregunta.
Sería interesante hacer un análisis de rendimiento de este código.
fuente
Para aquellos de ustedes que se preocupan por el orden (es decir, quieren
memcmp
que devuelva unint
como debería en lugar de nada), .NET Core 3.0 (y presumiblemente .NET Standard 2.1, también conocido como .NET 5.0) incluirá unSpan.SequenceCompareTo(...)
método de extensión (más unSpan.SequenceEqualTo
) que puede ser usado para comparar dosReadOnlySpan<T>
instancias (where T: IComparable<T>
).En la propuesta original de GitHub , la discusión incluyó comparaciones de enfoques con cálculos de tablas de salto, lectura de un
byte[]
aslong[]
, uso de SIMD y p / invocación a la implementación de CLRmemcmp
.En el futuro, este debería ser su método de referencia para comparar matrices de bytes o rangos de bytes (como debería usar en
Span<byte>
lugar debyte[]
sus API de .NET Standard 2.1), y es lo suficientemente rápido como para que ya no deba preocuparse por optimizarlo (y no, a pesar de las similitudes en el nombre, no funciona tan abismalmente como el horribleEnumerable.SequenceEqual
).fuente
Pensé en los métodos de aceleración de transferencia de bloques integrados en muchas tarjetas gráficas. Pero luego tendría que copiar todos los datos en bytes, por lo que esto no le ayuda mucho si no desea implementar una porción completa de su 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 la mayor cantidad de datos posible en un largo [] en lugar de 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 como valores largos [] o únicos largos. Luego, su ciclo de comparación solo necesitará 1/8 del número de iteraciones que tendría que hacer para un byte [] que contiene la misma cantidad de datos. Es una cuestión de cuándo y con qué frecuencia necesita comparar frente a cuándo y con qué frecuencia necesita acceder a los datos byte a byte, por ejemplo, para usarlos en una llamada API como parámetro en un método que espera un byte []. Al final, solo puede saber si realmente conoce el caso de uso ...
fuente
Es casi seguro que es mucho más lento que cualquier otra versión dada aquí, pero fue divertido de escribir.
fuente
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 las matrices tienden a estar cerca de la cola de las matrices. En las pruebas, descubrí que cuando este es el caso de 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:
fuente
Lo sentimos, 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 hacerlo.
Debe agregar algunas comprobaciones nulas iniciales y luego reutilizarlas como si estuviera en BCL.
fuente
Use
SequenceEquals
para esto para comparar.fuente
Si está buscando un comparador de igualdad de matriz de bytes muy rápido, le sugiero que eche un vistazo a este artículo de STSdb Labs: Comparador de igualdad de matriz de bytes.Cuenta con algunas de las implementaciones más rápidas para la comparación de igualdad de matriz byte [], que se presentan, se prueban y resumen el rendimiento.
También puede centrarse en estas implementaciones:
BigEndianByteArrayComparer - comparador de matriz de byte rápido [] de izquierda a derecha (BigEndian) BigEndianByteArrayEqualityComparer - - comparador de igualdad de byte rápido [] de izquierda a derecha (BigEndian) LittleEndianByteArrayComparer - byte rápido [] comparador de matriz de derecha a izquierda (LittleEndian) LittleEndianByteArrayEqualityComparer - octeto rápido [] comparador de igualdad de derecha a izquierda (LittleEndian)
fuente
La respuesta corta es esta:
De esta manera, puede usar la comparación de cadenas optimizada de .NET para hacer una comparación de matriz de bytes sin la necesidad de escribir código inseguro. Así es como se hace en segundo plano :
fuente
Compare(new byte[]{128}, new byte[]{ 255 }) == true
no tiene errores en absoluto ...Dado que muchas de las soluciones sofisticadas anteriores no funcionan con UWP y porque amo a Linq y los enfoques funcionales, le presento mi versión de este problema. Para escapar de la comparación cuando se produce la primera diferencia, elegí .FirstOrDefault ()
fuente
IndexOutOfRangeException
al comparar las matrices no vacías porque se está accediendo a los elementos1
a través deba0.Length
cuando debería ser0
a travésba0.Length - 1
. Si arreglas eso conEnumerable.Range(0, ba0.Length)
él, aún se devuelve incorrectamentetrue
para matrices de igual longitud donde solo los primeros elementos difieren porque no puedes distinguir entre los primeros elementos que satisfacenpredicate
y ningún elemento que satisfacepredicate
;FirstOrDefault<int>()
vuelve0
en ambos casos.