2011-09-07 11 views
5

Tengo este programa Java que utilizo para atrapar varios terabytes de datos. El rendimiento es una preocupación.¿Norma eficiente en Java?

que he perfilado de la aplicación, y una gran parte de todas las asignaciones de memoria, así como una gran fracción de tiempo de CPU provenir de la ejecución de una operación simple:

tengo una serie de caracteres ASCII. Sé que los caracteres del desplazamiento i para compensar j representan un número de coma flotante. Necesito extraer ese número de coma flotante en un double.

La ingenua Double.parseDouble(new String(buf, i, j - i)) hace el trabajo. Sin embargo, aquí es donde se gasta mucho tiempo y una gran cantidad de asignaciones de memoria viene, probablemente porque:

  • new String() crea un nuevo objeto, crea una matriz interna char[] y copia los caracteres en la matriz;
  • Double.parseDouble() crea un objeto FloatingDecimal, y también crea una matriz char[], también copiar los caracteres en ella.

Todas estas asignaciones y todas estas copias no son realmente necesarias. ¿Puedo evitarlos?

Lo que realmente me gustaría es una función strtod -como que tomaría un char[] (o una byte[]), así como las compensaciones de inicio/final, y devolver una double.

¿Alguna sugerencia? ¿Debería lanzar el mío? ¿Debo escribir un contenedor JNI alrededor del strtod? ¿Debería usar alguna biblioteca Java que ya esté disponible?

+0

En realidad, el método String.substring no copia el arreglo inicial. Puede ser útil si el constructor de cadenas es un cuello de botella. –

Respuesta

5

Lo que he hecho en el pasado es escribir un analizador para ByteBuffer (para evitar la conversión de bytes a la codificación de caracteres) a doble y viceversa. Si puede evitar la creación de cualquier objeto, puede ser mucho más rápido. Este enfoque funciona para los archivos mapeados en memoria, lo que también evita algunos costos de copia.

El código del núcleo tiene el siguiente aspecto. No maneja exponentes, pero puede agregar eso.

@Override 
public double read() throws BufferUnderflowException { 
    long value = 0; 
    int exp = 0; 
    boolean negative = false; 
    int decimalPlaces = Integer.MIN_VALUE; 
    while (true) { 
    byte ch = buffer.get(); 
    if (ch >= '0' && ch <= '9') { 
     while (value >= MAX_VALUE_DIVIDE_10) { 
     value >>>= 1; 
     exp++; 
     } 
     value = value * 10 + (ch - '0'); 
     decimalPlaces++; 
    } else if (ch == '-') { 
     negative = true; 
    } else if (ch == '.') { 
     decimalPlaces = 0; 
    } else { 
     break; 
    } 
    } 

    return asDouble(value, exp, negative, decimalPlaces); 
} 

The full code

que se detenga tan pronto como se pone ningún byte que no espera, por ejemplo, un , o \n

+0

(+1) ¡Agradable, gracias por compartir! – NPE

+0

También hay un código para codificar un doble como un ByteBuffer. –

5

me vería en la fuente para java.lang.Double, copiar el código que hace parseDouble a mi propia clase de ayuda y modificarlo para trabajar en char[] con offset y length directamente.

+0

Esta es una opción, excepto que esto es básicamente lo que 'FloatingDecimal' hace y se trata de ~ 3K líneas de código con muchas asignaciones de memoria dispersas por todas partes. Realmente no me gusta piratearlo si puedo evitarlo (la ruta JNI suena mucho más atractiva). – NPE

1

Si conoce una implementación eficiente de C, podría escribir una envoltura con JNI.

+0

Sin embargo, agregaría sobrecarga de JNI (supongo que tiene algún costo). – Thilo

+0

Si se trata de una función estática, la sobrecarga es probablemente bastante razonable. ¡La única manera de descubrirlo es probarlo! –

2

Por curiosidad he copiado la función strtod en Java y tiene ~ 10 aceleración tiempo comparando con el método Double.parseDouble (String) (incluso sin crear nuevas cadenas en bucle). Pero tal vez eso no sea suficiente para su implementación.

evaluación comparativa Micro da:

Double.parseDouble(): Las conversiones de 1.6M/segundo
Java strtod() Método: Las conversiones de 10.5m/segundo

+0

(+1) Impresionante, gracias por hacer esto. Fuera de interés, ¿qué implementación de 'strtod' tomaste? – NPE

+1

desde este enlace: [http://svn.ruby-lang.org/repos/ruby/branches/ruby_1_8/missing/strtod.c](http://svn.ruby-lang.org/repos/ruby/branches /ruby_1_8/missing/strtod.c) – styken

Cuestiones relacionadas