language agnostic - ¿Cuál es la forma más rápida de verificar si los archivos son idénticos?
language-agnostic file (17)
Si tiene 1,000,0000 archivos de origen, sospecha que son todos iguales, y desea compararlos, ¿cuál es el método de ayuno actual para comparar esos archivos? Asumir que son archivos Java y la plataforma donde se realiza la comparación no es importante. Cksum me está haciendo llorar. Cuando quiero decir idéntico, quiero decir TODO idéntico.
Actualización: sé sobre la generación de sumas de comprobación. diff es risible ... quiero velocidad.
Actualización: No te quedes atascado en el hecho de que son archivos de origen. Pretenda, por ejemplo, que tomó un millón de ejecuciones de un programa con una salida muy regulada. Quieres probar que todas las 1,000,000 versiones de la salida son iguales.
Actualización: leer el número de bloques en lugar de bytes? Inmediatamente tirar esos? ¿Es eso más rápido que encontrar el número de bytes?
Actualización: ¿ES CUALQUIERA diferente a la manera más rápida de comparar dos archivos?
¿Por qué reinventar la rueda? ¿Qué tal una aplicación de terceros? Es cierto que no tiene API, pero no creo que te pongas en esta situación a menudo. Me gusta esta aplicación doublekiller simplemente haga una copia de seguridad antes de comenzar. :) Es rápido y gratis!
Acabo de escribir ac # app que hace algo similar a lo que quieres. Lo que hace mi código es esto.
Lea todos los tamaños de cada archivo en una lista o matriz.
Use un bucle for para verificar si alguno de estos tamaños es el mismo. si son del mismo tamaño, compare un byte de un archivo con un byte del otro archivo. Si los dos bytes son iguales, muévase al siguiente byte. Si se encuentra una diferencia, devuelva que los archivos son diferentes.
Si se llega al final de ambos archivos y los dos bytes finales son iguales, los archivos deben ser idénticos.
He experimentado comparando hashes MD5 de archivos en lugar de pasar byte por byte, y he encontrado que con este método a menudo se pierden archivos idénticos, sin embargo, es significativamente más rápido.
Bueno, el algoritmo más óptimo dependerá de la cantidad de archivos duplicados.
Suponiendo que algunos son iguales, pero la mayoría son diferentes y los archivos son grandes.
Filtre los que obviamente no son los mismos con una simple verificación de la longitud del archivo.
Elija bytes aleatorios del archivo, calcule un hash y compare (minimizando las búsquedas de discos)
Síguelo con un archivo completo SHA1.
El hash MD5 sería más rápido que la comparación, pero más lento que un control CRC normal. Tienes que averiguar el tipo de fiabilidad que quieres en comparación.
En mi opinión, esta es una operación de sistema de archivos. Así que primero, elige tu sistema de archivos con cuidado. A continuación, deduplicar. Luego compara los inodos. Me gusta:
% find / -inum "$(ls -di "./test.file" | grep -E ''^[0-9]*'')"
<list of identical files provided in a few seconds to a minute>
Hay una serie de programas que comparan un conjunto de archivos en general para encontrar los idénticos. FDUPES es una buena: Link . Un millón de archivos no deberían ser un problema, dependiendo de la naturaleza exacta de la entrada. Creo que FDUPES requiere Linux, pero hay otros programas similares para otras plataformas.
Intenté escribir un programa más rápido, pero a excepción de los casos especiales, FDUPES fue más rápido.
De todos modos, la idea general es comenzar por verificar los tamaños de los archivos. Los archivos que tienen diferentes tamaños no pueden ser iguales, por lo que solo necesita mirar grupos de archivos del mismo tamaño. Luego, se vuelve más complicado si desea obtener un rendimiento óptimo: si es probable que los archivos sean diferentes, debe comparar partes pequeñas de los archivos, con la esperanza de encontrar las diferencias antes, de modo que no tenga que leer el resto. Sin embargo, si es probable que los archivos sean idénticos, puede ser más rápido leer cada archivo para calcular una suma de comprobación, ya que entonces puede leer secuencialmente desde el disco en lugar de saltar entre dos o más archivos. (Esto supone discos normales, por lo que SSD: s puede ser diferente).
En mis puntos de referencia, cuando intenté hacer un programa más rápido (para mi sorpresa) resultó ser más rápido leer primero cada archivo para calcular una suma de comprobación, y luego, si las sumas de comprobación eran iguales, compare los archivos directamente leyendo bloques alternativamente de cada archivo, que simplemente leer bloques alternativamente sin los cálculos de suma de comprobación anteriores! Resultó que al calcular las sumas de comprobación, Linux almacenaba en caché ambos archivos en la memoria principal, leía cada archivo de forma secuencial y las segundas lecturas eran muy rápidas. Al comenzar con lecturas alternas, los archivos no se leían (físicamente) de forma secuencial.
EDITAR:
Algunas personas han expresado su sorpresa y hasta la duda de que podría ser más rápido leer los archivos dos veces que leerlos solo una vez. Tal vez no pude explicar muy claramente lo que estaba haciendo. Estoy hablando de la precarga de la memoria caché, para tener los archivos en la memoria caché del disco cuando más tarde se acceda a ellos de una manera que sería lento en la unidad de disco físico. Here hay una página web donde he tratado de explicar más detalladamente, con imágenes, código C y mediciones.
Sin embargo, esto tiene (en el mejor de los casos) una relevancia marginal para la pregunta original.
La mayoría de las personas en sus respuestas están ignorando el hecho de que los archivos deben compararse repetidamente. Por lo tanto, las sumas de comprobación son más rápidas ya que la suma de comprobación se calcula una vez y se almacena en la memoria (en lugar de leer los archivos secuencialmente n veces).
No creo que el hashing sea más rápido que las comparaciones byte a byte. La comparación byte por byte se puede optimizar un poco al canalizar la lectura y la comparación de los bytes, y también se pueden comparar varias secciones del archivo en subprocesos paralelos. Sería algo como esto:
- Compruebe si los tamaños de los archivos difieren
- Leer bloques de los archivos en la memoria de forma asíncrona.
- Manejarlos en hilos de trabajo para hacer las comparaciones.
O simplemente ejecute un cmp (o el equivalente para su sistema operativo) en paralelo. Esto se puede escribir fácilmente y aún se obtiene el beneficio del paralelismo.
Primero compara las longitudes de archivo de todos los millones. Si tiene una forma barata de hacerlo, comience con los archivos más grandes. Si todos pasan eso, entonces compare cada archivo usando un patrón de división binario; esto fallará más rápido en archivos que son similares pero no iguales. Para obtener información sobre este método de comparación, consulte el método de Knuth-Morris-Pratt .
Si desea comparar archivos uno por uno, use ExamDiff.
Suponiendo que la expectativa es que los archivos serán los mismos (parece que ese es el escenario), entonces tratar con sumas de comprobación / hashes es una pérdida de tiempo. Es probable que sean iguales y que tenga que volver a hacerlo. lea los archivos para obtener la prueba final (también asumo que, dado que usted quiere "probar que son lo mismo", que tenerlos con el mismo valor no es suficiente).
Si ese es el caso, creo que la solución propuesta por David es muy similar a lo que tendrías que hacer. Un par de cosas que se podrían hacer para optimizar la comparación, en un nivel de complejidad creciente:
- Compruebe si los tamaños de archivo son los mismos antes de hacer la comparación
- use el memcmp () más rápido que pueda (comparando palabras en lugar de bytes; la mayoría de los tiempos de ejecución de C ya deberían hacer esto)
- use varios subprocesos para hacer comparaciones con el bloque de memoria (hasta el número de procesadores disponibles en el sistema, lo que provocaría que su subproceso luchara entre sí)
- use E / S asíncrona superpuesta para mantener los canales de E / S tan ocupados como sea posible, pero también perfile con cuidado para que trabaje entre los archivos lo menos posible (si los archivos se dividen entre varios discos y puertos de E / S diferentes, todos el mejor)
Usa el concepto de Bloom Filter. Una explicación simple aquí: http://crzyjcky.com/2013/01/03/the-magical-bloom-filter/
Te da tiempo constante de comparación. Sin embargo, este método no se puede utilizar solo. Apache Cassandra y HBase están utilizando esta técnica internamente.
Básicamente te dice que los archivos no son idénticos de manera muy rápida. Si dice que el archivo es idéntico, tiene que hacer otra ronda de verificación usando un método confiable.
Usar cksum
no es tan confiable como usar algo como md5sum
. Pero optaría por la máxima confiabilidad, lo que significa una comparación byte por byte usando cmp
.
Debe leer cada byte en ambos archivos para todos los métodos de verificación, por lo que también puede optar por el más confiable.
Como primer paso, puede consultar la lista del directorio para ver si los tamaños son diferentes. Esa es una forma rápida de obtener comentarios más rápidos para diferentes archivos.
Yo correría algo como esto
find -name /*.java -print0 | xargs -0 md5sum | sort
a continuación, ver qué archivos tienen diferentes sumas MD5. Esto agrupará los archivos por suma de comprobación.
Puede reemplazar md5sum which sha1sum o incluso rmd160 si lo desea.
más allá de la comparación, sincronice dos carpetas, super rápido! Lo usamos todo el tiempo, todos los días.
Actualización: No te quedes atascado en el hecho de que son archivos de origen. Pretenda, por ejemplo, que tomó un millón de ejecuciones de un programa con una salida muy regulada. Quieres probar que todas las 1,000,000 versiones de la salida son iguales.
Si tiene control sobre la salida, haga que el programa que crea los archivos / salida cree un md5 sobre la marcha e insértelo en el archivo o flujo de salida, o incluso canalice la salida a través de un programa que crea el md5 en el camino y lo almacena. los datos de alguna manera, el punto es hacer los cálculos cuando los bytes ya están en la memoria.
Si no puede hacer esto, entonces, como han dicho otros, verifique el tamaño de los archivos, luego haga una comparación byte a byte en archivos del mismo tamaño, no veo cómo cualquier tipo de división binaria o cálculo md5 es mejor que una recta En comparación, tendrá que tocar cada byte para probar la igualdad de cualquier manera que lo corte, de modo que también podría reducir la cantidad de cómputo necesaria por byte y obtener la capacidad de cortar tan pronto como encuentre una falta de coincidencia.
el cálculo de md5 sería útil si planea compararlos de nuevo más tarde con los nuevos resultados, pero básicamente volverá a mi primer punto de calcular el md5 lo antes posible.
Optaría por algo como el enfoque adoptado por el programa cmp
: abrir dos archivos (por ejemplo, el archivo 1 y el archivo 2), leer un bloque de cada uno y compararlos byte por byte. Si coinciden, lea el siguiente bloque de cada uno, compárelos byte por byte, etc. Si llega al final de ambos archivos sin detectar ninguna diferencia, busque el principio del archivo 1, cierre el archivo 2 y abra el archivo 3 en su lugar, y repita hasta que haya revisado todos los archivos. No creo que haya ninguna manera de evitar leer todos los bytes de todos los archivos si en realidad son todos idénticos, pero creo que este enfoque es (o está cerca de) la forma más rápida de detectar cualquier diferencia que pueda existir.
Modificación de OP : Levantó un comentario importante de Mark Bessey
"otra optimización obvia, si se espera que los archivos sean en su mayoría idénticos, y si son relativamente pequeños, es mantener uno de los archivos completamente en la memoria. Eso reduce el problema al tratar de leer dos archivos a la vez".