¿Alguna sugerencia sobre cómo mejorar el rendimiento de una conversión de Java String a byte []?

StackOverflow https://stackoverflow.com/questions/1023701

Pregunta

Heredé un fragmento de código que hace un uso intensivo de String - > Byte [] conversiones y viceversa para algún código de serialización de cosecha propia. Esencialmente, los objetos Java saben cómo convertir sus partes constituyentes en cadenas que luego se convierten en un byte []. Dicha matriz de bytes se pasa a través de JNI en código C ++ que reconstituye el byte [] en cadenas Cd std :: y las usa para arrancar objetos C ++ que reflejan los objetos Java. Hay un poco más, pero esta es una vista de alto nivel de cómo funciona este código; La comunicación funciona así en ambas direcciones, de modo que C ++ - > La transición de Java es una imagen especular de Java - > Transición en C ++ que mencioné anteriormente.

Una parte de este código, la conversión real de una cadena en un byte [], se muestra inesperadamente en el generador de perfiles como una gran cantidad de CPU. De acuerdo, hay muchos datos que se están transfiriendo, pero este es un cuello de botella inesperado.

El esquema básico del código es el siguiente:

public void convertToByteArray(String convert_me, ByteArrayOutputStream stream)
{
  stream.write(convert_me.getBytes());
}

Hay un poco más de la función pero no mucho. La función anterior se llama una vez para cada objeto String / Stringified y después de que todos los constituyentes se escriben en ByteArrayOutputStream, ByteArrayOutputStream se convierte en un byte []. Desglosar lo anterior en una versión más amigable para el perfilador extrayendo la llamada convert_me.getBytes () muestra que más del 90% del tiempo en esta función se gasta en la llamada getBytes ().

¿Hay alguna forma de mejorar el rendimiento de la llamada a getBytes () o hay otra forma potencialmente más rápida de lograr la misma conversión?

El número de objetos que se están convirtiendo es bastante grande. En las ejecuciones de creación de perfiles que utilizan solo un pequeño subconjunto de los datos de producción, veo algo así como más de 10 millones de llamadas a la función de conversión anterior.

Debido al hecho de que estamos muy cerca de lanzar el proyecto a producción, existen algunas soluciones que no son posibles en este momento:

  • Vuelva a escribir la interfaz de serialización para simplemente pasar objetos de cadena a través de la capa JNI. Esta es la forma obvia (para mí) de mejorar la situación, pero requeriría una reingeniería importante de la capa de serialización. Dado el hecho de que vamos a ingresar a UAT a principios de esta semana, es demasiado tarde para hacer este tipo de cambio complejo. Es mi tarea principal para el próximo lanzamiento, por lo que se hará; Sin embargo, necesito una solución hasta entonces, pero hasta ahora el código funciona, se ha utilizado durante años y la mayoría de los problemas han funcionado. Bueno, aparte de la actuación.
  • Cambiar la JVM (actualmente 1.5) tampoco es una opción. Desafortunadamente, esta es la JVM predeterminada que está instalada en las máquinas del cliente y desafortunadamente no es posible actualizar a 1.6 (que podría o no ser más rápido en este caso). Cualquiera que haya trabajado en grandes organizaciones probablemente entienda por qué ...
  • Además de esto, ya nos encontramos con restricciones de memoria, por lo que intentar almacenar en caché al menos las cadenas más grandes y su representación de matriz de bytes, aunque es una solución potencialmente elegante, es probable que cause más problemas de los que resolverá
¿Fue útil?

Solución

Supongo que parte del problema puede ser que una cadena Java está en formato UTF-16, es decir, dos bytes por carácter; entonces getBytes () está haciendo un montón de trabajo para convertir cada elemento UTF-16 en uno o dos bytes, dependiendo de su conjunto de caracteres actual.

¿Ha intentado usar CharsetEncoder : esto debería darle más control sobre la codificación de String y permitirle omitir parte de la sobrecarga en la implementación predeterminada de getBytes .

Alternativamente, ¿ha intentado especificar explícitamente el juego de caracteres en getBytes y ha utilizado US-ASCII como conjunto de caracteres?

Otros consejos

Veo varias opciones:

  • Si tiene cadenas Latin-1, podría dividir el byte superior de los caracteres en la cadena (Charset también hace esto, creo)
  • También podría dividir el trabajo entre varios núcleos si tiene más (el marco de la bifurcación tenía una copia de seguridad de 1.5 una vez)
  • También podría construir los datos en un generador de cadenas y solo convertirlos en una matriz de bytes una vez al final.
  • Mire su uso de GC / memoria. Demasiada utilización de la memoria podría ralentizar sus algoritmos debido a las frecuentes interrupciones de GC
  • Si son las mismas cadenas que convierte todas las veces, puede almacenar en caché el resultado en un WeakHashMap.

    Además, eche un vistazo al método getBytes () (la fuente está disponible si instala el SDK) para ver qué hace exactamente.

    El problema es que todos los métodos en Java, incluso hoy en día, asignan memoria con la producción UTF-8. Para obtener el rendimiento de codificación, necesitaría escribir código personalizado y reutilizar el búfer byte []. Colfer puede generar el código o simplemente copiar su implementación.

    https://github.com/pascal /colfer/blob/4c6f022c5183c0aebb8bc73e8137f976d31b1083/java/gen/O.java#L414

    Licenciado bajo: CC-BY-SA con atribución
    No afiliado a StackOverflow
    scroll top