2010-10-01 11 views
6

Estoy usando el siguiente código para hacer una suma de comprobación de un archivo que funciona bien. Pero cuando genero un hash para un archivo grande, digamos 2 GB, es bastante lento. ¿Cómo puedo mejorar el rendimiento de este código?Mejore el rendimiento de SHA-1 ComputeHash

fs = new FileStream(txtFile.Text, FileMode.Open); 
     formatted = string.Empty; 
     using (SHA1Managed sha1 = new SHA1Managed()) 
     { 
      byte[] hash = sha1.ComputeHash(fs); 

      foreach (byte b in hash) 
      { 
       formatted += b.ToString("X2"); 
      } 
     } 
     fs.Close(); 

Actualizar:

Sistema:

OS: Windows 7 de 64 bits, la CPU: I5 750, RAM: 4 GB, disco duro: 7200 rpm

Pruebas:

Prueba1 = 59.895 segundos

Test2 = 59.94 segundos

+1

+1 solo por tratar de mejorar el rendimiento del bit más pesado, y no preocuparse de que el formato esté construido de una manera relativamente ineficiente :) –

+0

:) probablemente debería cambiar eso a un generador de cadenas? –

+0

¡Ah, ahora estás hablando de ese +1! Sin embargo, lo que puede valer la pena si produce tales cadenas hexagonales con la suficiente frecuencia es tener un método que lo haga (buen caso para un método de extensión). Una vez que se utilice potencialmente en algún lugar donde el rendimiento hará una diferencia más real, sería más útil mover el StringBuilder (creado en la capacidad adecuada) o acercarse a la matriz de tamaño fijo. –

Respuesta

3

La primera pregunta es para qué necesita esta suma de comprobación. Si no necesita las propiedades criptográficas, entonces un hash no criptográfico o un hash que sea menos criptográficamente seguro (MD5 está "roto" no impide que sea un buen hash, ni lo suficientemente fuerte para algunos usos). Probablemente sea más eficiente. Podrías hacer tu propio hash leyendo un subconjunto de los datos (te aconsejo que este subconjunto funcione en fragmentos de 4096 bytes del archivo subyacente, ya que eso coincidiría con el tamaño del búfer utilizado por SHA1Managed y también permitiría leer un fragmento más rápido que lo haría si dijera cada X bytes para algún valor de X).

Editar: Un voto activo que me recuerda esta respuesta, también me ha recordado que escribí SpookilySharp que proporciona hashes de alto rendimiento de 32, 64 y 128 bits que no son criptográficos, pero son buenos para proporcionar sumas de comprobación contra errores , almacenamiento, etc. (Esto a su vez me ha recordado que debería actualizarlo para admitir .NET Core).

Por supuesto, si desea que el SHA-1 del archivo interopere con otra cosa, se queda atascado.

Experimentaría con diferentes tamaños de buffer, ya que aumentar el tamaño del buffer de filestream puede aumentar la velocidad a costa de memoria extra. Aconsejaría un múltiplo completo de 4096 (4096 es el valor predeterminado, por cierto) ya que SHA1Managed pedirá 4096 fragmentos a la vez, y de esta manera no habrá ningún caso en que FileStream devuelva menos de lo que se solicita (permitido pero a veces subóptimo) o hace más de una copia a la vez.

+0

+1 para la primera secuencia. A veces estamos solucionando el problema equivocado por completo. –

+0

Gracias. Decidí ir con MD5 ya que solo estaba verificando la integridad de los archivos después de la transmisión y no requería la seguridad adicional de SHA-1. Solo por curiosidad. Encontré la nueva implementación de SHA-1 de Intel usando instrucciones SSE3. http://software.intel.com/en-us/articles/improving-the-performance-of-the-secure-hash-algorithm-1/ ¿Me pregunto si y cómo se puede usar en el código administrado? –

1

Bueno, ¿está ligado a IO o a CPU? Si está vinculado a la CPU, no hay mucho que podamos hacer al respecto.

Es posible que la apertura de la FileStream con diferentes parámetros permitiría que el sistema de archivos para hacer más buffering o asume que vas a leer el archivo secuencial - pero dudo que le ayudará muy mucho. (Ciertamente, no va a hacer mucho si está vinculado a la CPU.)

¿Qué tan lento es "bastante lento" de todos modos? ¿Comparado con, por ejemplo, copiar el archivo?

Si tiene mucha memoria (por ejemplo, 4 GB o más) ¿cuánto tiempo lleva el archivo hash por segunda vez, cuando puede estar en la memoria caché del sistema de archivos?

+0

He realizado algunas pruebas de velocidad. Revisa mi actualización Además, el uso de CPU solo llega al 30%. –

+1

@Bruce: ¿30% en total? ¿De cuántos núcleos? Si se trata de una CPU multinúcleo pero con un algoritmo hash de subproceso único, aún podría estar vinculado a CPU. Mire la pestaña de rendimiento del Administrador de tareas para ver si una CPU está vinculada durante todo el tiempo :) –

+0

No, los 4 núcleos tienen un promedio de 5 - 6%. 2 núcleos haciendo un poco de trabajo, pero no están ni cerca de ser vinculados. Definitivamente vinculado a IO, ¿verdad? –

1

Antes que nada, ¿ha medido "bastante lento"? De this site, SHA-1 tiene aproximadamente la mitad de la velocidad de MD5 con aproximadamente 100 MB/s (dependiendo de la CPU), por lo que 2 GB demorarían unos 20 segundos en el hash. Además, tenga en cuenta que si está utilizando un HDD lento, este podría ser su verdadero cuello de botella, ya que 30-70 MB/s no son inusuales.

Para acelerar las cosas, es posible que no solo hash todo el archivo, pero las primeras X KB o partes representables de la misma (las partes que probablemente difieran). Si sus archivos no son muy similares, esto no debería causar duplicados.

1

Primero: el hash del archivo SHA-1 debe estar vinculado a E/S en CPU no antiguas, y I5 ciertamente no califica como antiguo. Por supuesto, depende de la implementación de SHA-1, pero dudo que SHA1Managed sea über-slow.

A continuación, 60seg para datos de 2GB es ~ 34MB/s - eso es lento para lecturas de disco duro; incluso un disco portátil de 2.5 "puede leer más rápido que eso. Suponiendo que el disco duro es interno (sin USB2/lo que sea o cuello de botella de red), y no hay muchas otras actividades de E/S de disco, me sorprendería ver menos de 60 MB/s de lectura desde una unidad moderna.

Mi conjetura sería que ComputeHash() utiliza un pequeño búfer internamente. Trate manualmente lectura/hash, por lo que puede especificar un búfer mayor (64kb o incluso mayor) para aumentar el rendimiento. usted también podría mover al procesamiento asíncrono modo de disco de lectura y cálculo se puede solapar.

-1

se puede utilizar esta lógica para conseguir SHA-1 valor. lo estaba utilizando en java.

sha1Calculate public class {

public static void main(String[] args)throws Exception 
    { 
     File file = new File("D:\\Android Links.txt"); 
     String outputTxt= ""; 
     String hashcode = null; 

     try { 

      FileInputStream input = new FileInputStream(file); 

      ByteArrayOutputStream output = new ByteArrayOutputStream(); 
      byte [] buffer = new byte [65536]; 
      int l; 

      while ((l = input.read (buffer)) > 0) 
       output.write (buffer, 0, l); 

      input.close(); 
      output.close(); 

      byte [] data = output.toByteArray(); 


       MessageDigest digest = MessageDigest.getInstance("SHA-1"); 

      byte[] bytes = data; 

      digest.update(bytes, 0, bytes.length); 
      bytes = digest.digest(); 

      StringBuilder sb = new StringBuilder(); 

      for(byte b : bytes) 
      { 
       sb.append(String.format("%02X", b)); 
      } 

       System.out.println("Digest(in hex format):: " + sb.toString()); 


     }catch (FileNotFoundException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } catch (IOException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } catch (NoSuchAlgorithmException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } 

    } 
0

Ni se SHA1Managed la mejor opción para grandes cadenas de entrada, ni se Byte.ToString ("X2") de la manera más rápida de convertir la matriz de bytes en una cadena.

Acabo de terminar un artículo con puntos de referencia detallados sobre ese tema. Compara SHA1Managed, SHA1CryptoServiceProvider, SHA1Cng y también considera SHA1.Create() en diferentes cadenas de entrada de longitud.

En la segunda parte, muestra 5 métodos diferentes para convertir la matriz de bytes en una cadena donde Byte.ToString ("X2") es el peor.

Mi entrada más grande fue de solo 10.000 caracteres, por lo que es posible que desee ejecutar mis puntos de referencia en su archivo de 2 GB. Sería bastante interesante si/cómo eso cambia los números.

http://wintermute79.wordpress.com/2014/10/10/c-sha-1-benchmark/

Sin embargo, para las comprobaciones de integridad de archivos que es mejor usar MD5 como ya escribió.