2011-05-22 13 views
7

estoy usando la siguiente función para calcular las sumas de comprobación de archivos:java: necesidad de aumentar el rendimiento de cálculo de comprobación

public static void generateChecksums(String strInputFile, String strCSVFile) { 
    ArrayList<String[]> outputList = new ArrayList<String[]>(); 
    try { 
     MessageDigest m = MessageDigest.getInstance("MD5"); 
     File aFile = new File(strInputFile); 
     InputStream is = new FileInputStream(aFile); 

     System.out.println(Calendar.getInstance().getTime().toString() + 
        " Processing Checksum: " + strInputFile); 

     double dLength = aFile.length(); 
     try { 
      is = new DigestInputStream(is, m); 
      // read stream to EOF as normal... 
      int nTmp; 
      double dCount = 0; 
      String returned_content=""; 
      while ((nTmp = is.read()) != -1) { 
       dCount++; 
       if (dCount % 600000000 == 0) { 
        System.out.println(". "); 
       } else if (dCount % 20000000 == 0) { 
        System.out.print(". "); 
       } 
      } 
      System.out.println(); 
     } finally { 
      is.close(); 
     } 
     byte[] digest = m.digest(); 
     m.reset(); 
     BigInteger bigInt = new BigInteger(1,digest); 
     String hashtext = bigInt.toString(16); 
     // Now we need to zero pad it if you actually/want the full 32 chars. 
     while(hashtext.length() < 32){ 
      hashtext = "0" + hashtext; 
     } 
     String[] arrayTmp = new String[2]; 
     arrayTmp[0] = aFile.getName(); 
     arrayTmp[1] = hashtext; 
     outputList.add(arrayTmp); 
     System.out.println("Hash Code: " + hashtext); 
     UtilityFunctions.createCSV(outputList, strCSVFile, true); 
    } catch (NoSuchAlgorithmException nsae) { 
     System.out.println(nsae.getMessage()); 
    } catch (FileNotFoundException fnfe) { 
     System.out.println(fnfe.getMessage()); 
    } catch (IOException ioe) { 
     System.out.println(ioe.getMessage()); 
    } 
} 

El problema es que el bucle para leer en el archivo es muy lento:

while ((nTmp = is.read()) != -1) { 
    dCount++; 
    if (dCount % 600000000 == 0) { 
     System.out.println(". "); 
    } else if (dCount % 20000000 == 0) { 
     System.out.print(". "); 
    } 
} 

Un archivo de 3 GB que tarda menos de un minuto en copiarse de una ubicación a otra, demora más de una hora en calcularse. ¿Hay algo que pueda hacer para acelerar esto o debería intentar ir en una dirección diferente como usar un comando de shell?

Actualización: Gracias a la sugerencia de trinquete monstruo He cambiado el código a este que es ridículamente más rápido (yo supongo 2048x más rápido ...):

byte[] buff = new byte[2048]; 
while ((nTmp = is.read(buff)) != -1) { 
    dCount += 2048; 
    if (dCount % 614400000 == 0) { 
     System.out.println(". "); 
    } else if (dCount % 20480000 == 0) { 
     System.out.print(". "); 
    } 
} 
+0

La idea es para indicar el progreso en la salida estándar. Esta era mi forma de emular el comportamiento "hash" de un cliente ftp de línea de comando. El conteo% 60000000 hace una impresión frente a una impresión. – opike

Respuesta

4

usar un buffer

byte[] buff = new byte[2048]; 
while ((nTmp = is.read(buff)) != -1) 
{ 
    dCount+=ntmp; 
    //this logic won't work anymore though 
    /* 
    if (dCount % 600000000 == 0) 
    { 
     System.out.println(". "); 
    } 
    else if (dCount % 20000000 == 0) 
    { 
     System.out.print(". "); 
    } 
    */ 
} 

editar: o si no necesita los valores, haga

while(is.read(buff)!=-1)is.skip(600000000); 

NVM al parecer, los ejecutores de DigestInputStream eran estúpidos y no probó todo correctamente antes de la liberación

+0

'DigestInputStream' no anula' skip() ', por lo que el digestor no procesará los bytes omitidos. – McDowell

+1

Aparentemente no leyó el Javadoc para DigestInputStream, donde * no * dice que el salto actualiza el resumen. Votado por comentario estúpido. – EJP

+0

tipo no dice _cualquier_ sobre omisión y tampoco verifica nullpointer en el resumen en la construcción, dejándote averiguar por qué obtienes un NPE en una transmisión envuelta en otro lugar –

2

¿Ha intentado quitar la década de println? ¡Me imagino que toda la manipulación de cadenas podría consumir la mayor parte del procesamiento!

Editar: No he leído con claridad, ahora me doy cuenta de lo poco frecuente que estarían de salida, me retracto de mi respuesta, pero supongo que no era totalmente inapreciable :-p

+2

La impresión solo se produce un pequeño% de las veces ... si algo está afectando el rendimiento, es la lógica condicional. – opike

+0

+1: Para tener en cuenta la salida de la consola. No será la única mejora de rendimiento en esta situación, pero muchos programadores no se dan cuenta de la sobrecarga que hay al escribir en la consola. Puede ralentizar una aplicación significativamente. –

+0

Downvoted porque "toda la manipulación de cadenas" solo ocurre una vez cada veinte millones de iteraciones. – QuantumMechanic

0
(Lo siento!)

El problema es que System.out.print se usa con demasiada frecuencia. Cada vez que se llama nuevo, se deben crear objetos String y es costoso.

Utilice la clase StringBuilder en su lugar o su subproceso seguro de seguridad StringBuffer.

StringBuilder sb = new StringBuilder(); 

Y cada vez que es necesario agregar algo llaman a esto:

sb.append("text to be added"); 

Más tarde, cuando ya está listo para imprimirlo:

system.out.println(sb.toString()); 
0

Francamente hay varios problemas con su código eso lo hace lento:

  1. Como dijo el fenómeno de trinquete, las lecturas de disco deben almacenarse en búfer porque Java read() probablemente se traduzcan a llamadas de IO del sistema operativo sin almacenamiento en memoria intermedia automático, por lo que uno read() es 1 llamada al sistema. El sistema operativo normalmente funcionará mucho mejor si utiliza una matriz como búfer o BufferedInputStream. Mejor aún, puede usar nio para asignar el archivo a la memoria y leerlo tan rápido como el sistema operativo pueda manejarlo.

  2. Puede que no lo crea, pero el contador dCount++; puede haber usado muchos ciclos. Creo que incluso para el último procesador Intel Core, se necesitan varios ciclos de reloj para completar un complemento de coma flotante de 64 bits.Sería mucho mejor usar un largo para este contador. Si el único propósito de este contador es mostrar el progreso, puede aprovechar el hecho de que los números enteros de Java se desbordan sin causar un error y avanzar su visualización de progreso cuando un tipo de char se ajusta a 0 (es decir, 65536 lecturas).

  3. El siguiente relleno de cadena también es ineficaz. Debe usar un StringBuilder o un Formatter.

    while (hashtext.length) hashtext = "0" + hashtext; }

  4. Trate de usar un generador de perfiles para encontrar más problemas de eficiencia en su código

Cuestiones relacionadas