2011-07-11 17 views
8

Me han encargado la lectura de archivos CSV grandes (más de 300k registros) y aplicar patrones de expresiones regulares a cada registro. Siempre he sido un desarrollador de PHP y nunca he probado ningún otro idioma, pero decidí que debería hacer una inmersión e intentar hacer esto con Java, que supuse que sería mucho más rápido.Regex de Java más lento de lo esperado

De hecho, solo leer el archivo CSV línea por línea fue 3 veces más rápido en Java. Sin embargo, cuando apliqué los requisitos de expresiones regulares, la implementación de Java demostró demorar un 10-20% más que el script PHP.

Es muy posible que haya hecho algo mal en Java, porque acabo de aprender esto como lo hice hoy. A continuación están los dos guiones, cualquier consejo sería muy apreciado. Realmente me gustaría no darme por vencido con Java para este proyecto en particular.

CÓDIGO PHP

<?php 
$bgtime=time(); 
$patterns =array(
    "/SOME REGEXP/", 
    "/SOME REGEXP/",      
    "/SOME REGEXP/",  
    "/SOME REGEXP/" 
); 

$fh = fopen('largeCSV.txt','r'); 
while($currentLineString = fgetcsv($fh, 10000, ",")) 
{ 
    foreach($patterns AS $pattern) 
    { 
     preg_match_all($pattern, $currentLineString[6], $matches); 
    } 
} 
fclose($fh); 
print "Execution Time: ".(time()-$bgtime); 

?> 

código Java

import au.com.bytecode.opencsv.CSVReader; 
import java.io.FileReader; 
import java.io.IOException; 
import java.util.regex.Pattern; 
import java.util.regex.Matcher; 
import java.util.ArrayList; 

public class testParser 
{ 
    public static void main(String[] args) 
    { 
     long start = System.currentTimeMillis(); 


     String[] rawPatterns = { 
        "SOME REGEXP", 
        "SOME REGEXP",      
        "SOME REGEXP",  
        "SOME REGEXP"  
     }; 

     ArrayList<Pattern> compiledPatternList = new ArrayList<Pattern>();   
     for(String patternString : rawPatterns) 
     { 
      Pattern compiledPattern = Pattern.compile(patternString); 
      compiledPatternList.add(compiledPattern); 
     } 


     try{ 
      String fileName="largeCSV.txt"; 
      CSVReader reader = new CSVReader(new FileReader(fileName)); 

      String[] header = reader.readNext(); 
      String[] nextLine; 
      String description; 

      while((nextLine = reader.readNext()) != null) 
      { 
       description = nextLine[6]; 
       for(Pattern compiledPattern : compiledPatternList) 
       { 
        Matcher m = compiledPattern.matcher(description); 
        while(m.find()) 
        { 
         //System.out.println(m.group(0)); 
        }     
       } 
      } 
     } 

     catch(IOException ioe) 
     { 
      System.out.println("Blah!"); 
     } 

     long end = System.currentTimeMillis(); 

     System.out.println("Execution time was "+((end-start)/1000)+" seconds."); 
    } 
} 
+0

No está completamente relacionado con su problema de expresión regular, pero es posible que desee consultar http://download.oracle.com/javase/6/docs/api/java/util/Scanner.html. Es posible que encuentre que su clase CSVReader no es necesaria. No crear todas esas cadenas temporales ('nextLine' parece tener al menos 7 cadenas, pero solo necesita una) podría mejorar el rendimiento. – wolfcastle

Respuesta

3

no veo nada manifiestamente equivocada con su código. Intente aislar el cuello de botella de rendimiento con un perfilador. Encuentro que el perfil netbeans es muy fácil de usar.

EDITAR: ¿Por qué especular? Perfile la aplicación y obtenga un informe detallado de dónde se gasta el tiempo. Luego trabaje para resolver las áreas ineficientes. Ver http://profiler.netbeans.org/ para más información.

EDIT2: OK, me aburrí y describí esto. Mi código es idéntica a la suya y analiza un archivo CSV con 1.000 líneas idénticas de la siguiente manera:

SOME REGEXP,SOME REGEXP,SOME REGEXP,SOME REGEXP,SOME REGEXP,SOME REGEXP,SOME REGEXP,SOME REGEXP,SOME REGEXP,SOME REGEXP 

Estos son los resultados (obviamente, los resultados serán diferentes ya que mis expresiones regulares son triviales). Sin embargo, es fácil ver que el procesamiento de expresiones regulares no es su principal área de preocupación.

enter image description here

Curiosamente, si aplico un BufferedReader, el rendimiento se ve reforzada por la friolera de 18% (ver más abajo).

enter image description here

+0

Acabo de batir esto en Notepad ++, pero le daré una oportunidad a netbeans y veré lo que indica. – IOInterrupt

+0

Aparentemente no sé cómo utilizar el generador de perfiles de manera efectiva. He ejecutado el generador de perfiles contra mi aplicación JAVA, pero todo lo que parece mostrarme es la Memoria (Heap), la Memoria (GC) y las Hilos/Clases Cargadas ... junto con el tiempo de ejecución de main(). ¿Hay algún buen tutorial sobre cómo utilizar esto? – IOInterrupt

+0

@IOInterrupt - En Netbeans, Perfil> Perfil de proyecto principal> CPU> Aplicación completa> Ejecute – hoipolloi

0

Algunos puntos a tener en cuenta aquí.

  1. Empieza a medir el tiempo incluso antes de compilar los patrones. Pattern.compile es una operación relativamente costosa y puede consumir más tiempo si el patrón es complejo. ¿Por qué no comenzar a medirlo después del paso de compilación?

  2. No estoy seguro de qué tan eficiente es la clase CSVReader.

  3. En lugar de imprimir directamente los resultados coincidentes en el hilo principal en sí, (como System.out.println es de bloqueo y costoso) podría quizás delegar la impresión a un hilo diferente.

+1

Prefiero cronometrar el script desde el inicio, porque ambos scripts básicamente hacen lo mismo y creo que el tiempo total de ejecución del script es un indicador válido. Pensé que la complejidad de la expresión regular era el problema, así que los cambié a todos para ser una sola palabra común. El tiempo de ejecución de PHP fue de 93 segundos frente a los 246 segundos de Java. Creo que la clase CSVReader es eficiente, ya que fue capaz de leer el archivo CSV mucho más rápido (3 veces más rápido) que la función PHP fgetcsv(). Además, he comentado la función println(). – IOInterrupt

+0

@IOInterrupt: Derecha. Hay una serie de factores que pueden venir a jugar aquí. La cantidad de entrada de memoria a la máquina virtual también juega un papel importante. Podría intentar perfilar la aplicación como lo sugirió hoipolloi. – adarshr

+0

Soy bastante ignorante cuando se trata de la máquina virtual. Estaba feliz de hacer que esto funcione. – IOInterrupt

0

varias cosas:

  1. La expresión regular tiene que ser compilado una sola vez y que debe estar en el inicio del servidor por lo que no tiene importancia para el rendimiento a la vez su funcionamiento.

  2. Y lo que es más importante, está escribiendo un punto de referencia completamente inválido para un programa java de larga ejecución. Seguramente estará cargando varias clases mientras hace benchmarking y, en general, solo prueba el rendimiento del intérprete y NO el JIT, lo que obviamente dará como resultado un rendimiento mucho peor. Ver this excelente publicación sobre cómo escribir un punto de referencia válido en java. Sin duda, esto solucionará todos los supuestos problemas de rendimiento en este caso.

+2

El OP no dijo que había un servidor, ni que era un programa de larga ejecución. Podría ser el caso, y entonces estarías en lo correcto, pero podría no ser así. –

+1

Supuse que era un servidor porque usaba PHP, pero sí, tiene razón. Pero si el programa no se ejecuta por mucho tiempo y no es crítico para el rendimiento, ¿por qué demonios uno se preocuparía por optimizarlo? – Voo

+0

El script PHP se ejecuta manualmente a través de la CLI de PHP al igual que la aplicación JAVA se está ejecutando manualmente. No estoy seguro si esto hace una diferencia en lo que estás sugiriendo. Ambos scripts se ejecutan durante aproximadamente 10 minutos cuando se procesa un archivo CSV de 500mb. – IOInterrupt

4

El uso de un lector tamponada podría mejorar el rendimiento conseguir un poco mejor:

CSVReader reader = new CSVReader(new BufferedReader(new FileReader(fileName))); 
+0

Buena captura, si FileReader no se guarda en el búfer, seguramente es un cuello de botella de rendimiento. – Voo

+0

Es más si los búfers de CSVReader son o no. –

+0

Esto dio un ligero aumento, pero no fue la salsa mágica. ¡Gracias por las sugerencias! – IOInterrupt

0

lo recomiendo:

  • como alguien ha sugerido, el perfil para ver donde el cuello de botella real es;
  • díganos cuáles son las expresiones regulares reales: es posible que esté utilizando algún subpatrón específico que no sea muy eficiente en la implementación de Java.

Es muy posible que las partes del motor de expresiones regulares de PHP estén más optimizadas que las de Java para tipos de expresión específicos, y/o que hay una manera de optimizar la expresión real que está utilizando.