que - qué es un flujo en java
Java: decodificación de flujo de caracteres multiproceso (4)
Estoy manteniendo un analizador de CSV de alto rendimiento y trato de aprovechar al máximo la última tecnología para mejorar el rendimiento. Para estas tareas particulares, esto significa:
- Memoria flash (poseemos una tarjeta PCI-Express relativamente económica, 1 TB de almacenamiento que alcanza un rendimiento de lectura sostenida de 1 GB / s)
- Núcleos múltiples (Tenemos un servidor Nehalem barato con 16 hilos de hardware)
La primera implementación del analizador CSV fue de un solo subproceso. Lectura de archivos, decodificación de caracteres, división de campos, análisis de texto, todo dentro del mismo hilo. El resultado fue un rendimiento de aproximadamente 50 MB / s. No está mal, pero está muy por debajo del límite de almacenamiento ...
La segunda implementación usa un hilo para leer el archivo (en el nivel de byte), un hilo para decodificar los caracteres (de ByteBuffer a CharBuffer) y múltiples hilos para analizar los campos (Me refiero al análisis de campos de texto delimitados en dobles, enteros, fechas ...). Esto funciona bien más rápido, cerca de 400 MB / s en nuestra caja.
Pero aún muy por debajo del rendimiento de nuestro almacenamiento. Y esos SSD mejorarán nuevamente en el futuro, no estamos aprovechando al máximo en Java. Está claro que la limitación actual es la decodificación de caracteres (CharsetDecoder.read (...)). Ese es el cuello de botella, en un poderoso procesador Nehalem transforma bytes en caracteres a 400MB / s, bastante bueno, pero tiene que ser de un solo hilo. El CharsetDecoder tiene cierto estado, dependiendo del juego de caracteres utilizado, y no admite la decodificación multiproceso.
Así que mi pregunta a la comunidad es (y gracias por leer la publicación hasta el momento): ¿alguien sabe cómo paralelizar la operación de decodificación del juego de caracteres en Java?
¿Alguien sabe cómo paralelizar la operación de decodificación del juego de caracteres en Java?
Es posible que pueda abrir varias secuencias de entrada para hacer esto (no estoy seguro de cómo hacerlo con NIO, pero debe ser posible).
Lo difícil que esto sería depende de la codificación desde la que está decodificando. Necesitará una solución a medida para la codificación del objetivo. Si la codificación tiene un ancho fijo (por ejemplo, Windows-1252), entonces un byte == un carácter y la decodificación es fácil.
Las codificaciones modernas de ancho variable (como UTF-8 y UTF-16) contienen reglas para identificar el primer byte de una secuencia de caracteres, por lo que es posible saltar al medio de un archivo y comenzar a decodificar (tendrá que anotar el final del fragmento anterior, por lo que es aconsejable comenzar a decodificar el final del archivo primero).
Es posible que algunas codificaciones heredadas de ancho variable no estén tan bien diseñadas, por lo que no tendrá más opción que decodificar desde el inicio de los datos y leerlos secuencialmente.
Si es una opción, genere sus datos como UTF-16BE. Luego puede cortar la decodificación y leer dos bytes directamente a un char.
Si el archivo es Unicode, ten cuidado con el manejo de la BOM, pero supongo que ya estás familiarizado con muchos de los detalles de bajo nivel.
Está claro que la limitación actual es la decodificación de caracteres (CharsetDecoder.read (...))
¿Como sabes eso? ¿Su monitoreo / perfil muestra concluyentemente que el hilo del decodificador está usando el 100% de uno de sus núcleos?
Otra posibilidad es que el sistema operativo no sea capaz de conducir el SSD a su velocidad teórica máxima.
Si la decodificación UTF-8 es definitivamente el cuello de botella, entonces debería ser posible hacer la tarea en paralelo. Pero ciertamente necesitará implementar sus propios decodificadores para hacer esto.
Si conoce la codificación y tiene un tamaño fijo o no contiene secuencias de bytes superpuestas, puede escanear una secuencia especial. En CSV, una secuencia para nuevas líneas podría tener sentido. Incluso si detecta dinámicamente la codificación, podría ejecutar un pase de los primeros pocos bytes para determinar la codificación, y luego pasar a la decodificación paralela.
Otra (loca) alternativa sería simplemente separar la entrada en fragmentos de algún tamaño arbitrario, ignorar los problemas de decodificación y luego decodificar cada uno de los fragmentos en paralelo. Sin embargo, debe asegurarse de que los fragmentos se superponen (con un tamaño parametrizado). Si la región superpuesta de los dos fragmentos se decodifica de la misma manera por los dos hilos (y la superposición era lo suficientemente grande para la codificación especificada), sería seguro unir los resultados. Cuanto mayor sea la superposición, mayor será el procesamiento requerido, y menor será la probabilidad de error. Además, si se encuentra en una situación en la que sabe que la codificación es UTF-8, o una codificación similarmente simple, puede establecer una superposición bastante baja (para ese cliente) y aún así garantizar el funcionamiento correcto.
Si el segundo trozo resulta ser incorrecto, tendrá que volver a hacerlo, por lo que es importante no hacer grandes trozos en paralelo. Si hace más de dos fragmentos en paralelo, sería importante ''reparar'' de principio a fin, de modo que un bloque desalineado no dé como resultado la invalidación del siguiente bloque (que podría estar alineado correctamente).