2010-05-07 26 views
12

Estamos trabajando para reducir la latencia y aumentar el rendimiento de un proceso escrito en Java que consume datos (cadenas xml) de un socket mediante el método readLine() de la clase BufferedReader. Los datos están delimitados por el separador de fin de línea (\ n), y cada línea puede tener una longitud variable (6KBits - 32KBits). Nuestro código es el siguiente:Java: Eficiencia del método readLine del BufferedReader y posibles alternativas

Socket sock = connection; 
InputStream in = sock.getInputStream(); 
BufferedReader inputReader = new BufferedReader(new InputStreamReader(in)); 
... 
do 
{ 
    String input = inputReader.readLine(); 
    // Executor call to parse the input thread in a seperate thread 
}while(true) 

Así que tengo un par de preguntas:

  • ¿El retorno inputReader.readLine método() tan pronto como llega el carácter \ n o va a esperar hasta el el búfer está lleno?
  • ¿Hay un más rápido de recogida de datos del zócalo que utilizando un BufferedReader?
  • ¿Qué sucede cuando el tamaño de la cadena de entrada es menor que el tamaño del búfer de recepción del zócalo?
  • ¿Qué sucede cuando el tamaño de la cadena de entrada es más grande que el tamaño del búfer de recepción del zócalo?

Estoy aprendiendo (lentamente) con las bibliotecas IO de Java, por lo que cualquier puntero es muy apreciado.

¡Gracias!

+0

supongo que está utilizando un formato específico de la aplicación - saltos de línea y espacios en blanco son, en general, no es significativo en XML. – mdma

+0

Sí, lo siento debería ser más claro.Estamos consumiendo datos de transmisión desde una aplicación externa a través de una conexión TCP. Cada mensaje XML está separado por un carácter \ n. Supongo que es idéntico a leer un archivo secuencialmente donde cada línea es un documento xml completamente formado. – Luhar

Respuesta

15

¿Volverá el método inputReader.readLine() tan pronto como llegue al carácter \ n o esperará hasta que el búfer esté lleno?

  • Volverá tan pronto como salga una nueva línea.

¿Hay un proceso de recolección de datos más rápido que el uso de un BufferedReader?

  • BufferedReader conlleva algunas copias de los datos. Puede probar las API de NIO, que pueden evitar copiar, pero es posible que desee crear un perfil antes de dedicar tiempo a esto para ver si realmente se trata de la E/S que es el cuello de botella.Una solución rápida más sencilla es agregar BufferedInputStream alrededor del zócalo, de modo que cada lectura no golpee el zócalo (No está claro si InputStreamReader realiza algún almacenamiento en búfer).

    nueva BufferedReader (nuevo InputStreamReader (nueva BufferedInputStream (en)))

¿Qué pasa cuando el tamaño de la cadena de entrada es menor que el tamaño del búfer de recepción del zócalo?

  • El BufferedReader obtendrá todos los datos disponibles. A continuación, escaneará estos datos para buscar la nueva línea. El resultado es que las lecturas posteriores ya pueden tener los datos en el BufferedReader.

¿Qué pasa cuando el tamaño de la cadena de entrada es más grande que el tamaño del búfer de recepción del zócalo?

  • El BufferedReader será leer lo que está en la memoria intermedia recieve, y ya que no hay nueva línea o el final de la corriente se alcanza, continuará para leer los datos de la toma hasta que encuentra EOF o una nueva línea. Las lecturas posteriores pueden bloquearse hasta que haya más datos disponibles.

En resumen, BufferedReader bloquea solo cuando es absolutamente necesario.

+0

Gracias por su respuesta detallada. – Luhar

+0

Sin preocupaciones. Espero que obtenga el rendimiento mejorado que está buscando con los cambios sugeridos. Si no, intente crear un perfil, y si aún no tiene suerte, siempre puede publicar otra pregunta pidiendo ayuda para mejorar el rendimiento :-) ¡Buena suerte! – mdma

2

La respuesta a su primera pregunta es sí y no. Si el búfer ya contiene el terminador de línea, volverá inmediatamente; sin embargo, si no contiene el terminador, intentará llenar el búfer, pero no necesariamente del todo. Solo se leerá hasta que se alcancen algunos datos nuevos (al menos un carácter) o EOF.

Una de las cosas buenas de Java es que las bibliotecas son de código abierto, por lo que si tiene una copia completa del JDK, puede consultar la fuente usted mismo para contestar este tipo de preguntas. Yo uso eclipse como mi IDE y de forma predeterminada si coloca el cursor sobre un nombre de clase y presiona F3, lo llevará a la fuente (así es como obtuve la respuesta anterior). La advertencia es con la distribución estándar, la fuente para algunas de las clases internas/código nativo no está disponible.

Para su segunda pregunta, diría generalmente que no, ya que la lógica utilizada por BufferedReader es generalmente la misma que cualquier código tendría que volver a crear para lograr la misma tarea. Lo único que podría desacelerar el BufferedReader es que internamente utiliza un StringBuffer, que está sincronizado, en lugar del StringBuilder no sincronizado.

3

Una de las ventajas de BufferedReader es que proporciona una capa de separación (el búfer) entre los métodos de entrada (lectura, línea de lectura, etc.) que utiliza y las lecturas reales del zócalo, por lo que no tiene que preocuparse por todos los casos como "la mayoría de la línea está en el búfer, pero necesita leer otro búfer para obtener el \ n" etc.

¿Ha realizado una medición de rendimiento que indique que usar un BufferedReader es un problema de rendimiento para su aplicación? De lo contrario, le sugiero que empiece seleccionando un método de entrada que proporcione la funcionalidad que desea (la entrada basada en línea termina por \ n, por el sonido), y preocúpese si hay una manera "más rápida" de hacerlo. solo si encuentra que el método de entrada es un cuello de botella.

Si la entrada basada en línea es realmente lo que está buscando, usted va a terminar usando algún tipo de tampón como BufferedReader hace, ¿por qué reinventar esta rueda?

+1

Gracias por su respuesta. Hemos realizado una gran cantidad de perfiles en la aplicación y descubrimos que puede haber un retraso de unos pocos milisegundos al procesar mensajes pequeños. ¡Dada la documentación API del BufferedReader, no parece tener sentido! Hemos desactivado el algoritmo de Nagle al configurar el indicador TcpNoDelay y estamos buscando otras alternativas. – Luhar

+0

Interesante. El BufferedReader sin duda implicará una copia adicional de los datos, pero es difícil ver cómo eso podría llevar milisegundos ... –

0

Si conoce la codificación de caracteres de los datos de entrada es posible que desee escribir su propia clase que realiza la lectura de los datos binarios, en busca de su terminador específico de fin de línea. Esto puede eliminar una gran cantidad de codificaciones/decodificaciones y copias innecesarias. Asegúrese de poner en práctica algo con una re-utilizable tampones (por ejemplo, de NIO CharBuffer o ByteBuffer clases que vienen a la mente, o una inicializado correctamente StringBuilder si necesita String casos). Asegúrate de tener suficiente espacio en el buffer, 32Ki a 64Ki no es nada para las computadoras actuales.

vez que haya recibido los datos en un recipiente utilizable se puede utilizar cualquier truco en el libro (múltiples hilos, ejecutores, etc.) para manejar los datos de forma eficiente. Recuerde, la única manera de desacelerar una CPU actual es accediendo a fallas de caché: conjuntos de datos grandes/dinámicos, copia espuria o ramas, bucles innecesarios, declaraciones if y además, por supuesto, llamadas al kernel y E/S.

+0

y McAfee, McAfee ralentiza * * todo a un arrastre :( –

Cuestiones relacionadas