2012-08-23 13 views
9

Estoy haciendo algunos benchmarks con un selector optimizado Java NIO en Linux sobre loopback (127.0.0.1).Secuencia mínima posible del selector Java NIO

Mi prueba es muy simple:

  • Un programa envía un paquete UDP a otro programa que se hace eco de vuelta al remitente y se calcula el tiempo de ida y vuelta. El siguiente paquete solo se envía cuando se anota el anterior (cuando vuelve). Se realiza un calentamiento adecuado con un par de millones de mensajes antes de que se realice el punto de referencia. El mensaje tiene 13 bytes (sin contar los encabezados UDP).

Para el tiempo de ida y vuelta consigo los siguientes resultados:

  • min Tiempo: 13 micros
  • Prometio: 19 micros
  • 75% percentil: 18,567 nanos
  • 90% percentil: 18,789 nanos
  • 99% percentil: 19,184 nanos
  • 99.9% percentil: 19,264 nanos
  • 99,99% percentil: 19.310 nanos
  • 99,999% percentil: 19.322 nanos

pero el problema aquí es que Estoy hilando 1 millón de mensajes.

si hilo sólo 10 mensajes que consiguen resultados muy diferentes:

  • min Tiempo: 41 micros
  • Tiempo medio: 160 micros
  • 75% percentil: 150,701 Nanos
  • 90% percentil : 155,274 nanos
  • 99% percentil: 159,995 nanos
  • 99,9% percentil: 159,995 nanos
  • 99,99% percentil: 159.995 nanos
  • 99,999% percentil: 159.995 nanos

Corrígeme si estoy equivocado, pero sospecho que una vez que tengamos el selector NIO girar los tiempos de respuesta se hacen óptima. Sin embargo, si estamos enviando mensajes con un intervalo lo suficientemente grande entre ellos, pagamos el precio de activar el selector.

Si juego con el envío de un solo mensaje consigo varias veces entre 150 y 250 micros.

Así que mis preguntas para la comunidad son:

1 - ¿Es mi tiempo mínimo de 13 micros con promedio de 19 micros óptimas para esta prueba de paquetes de ida y vuelta. Parece que estoy ganando ZeroMQ por lejos, así que me puede faltar algo aquí.A partir de este punto de referencia, parece que ZeroMQ tiene un tiempo promedio de 49 micros (99% percentil) en un núcleo estándar =>http://www.zeromq.org/results:rt-tests-v031

2 - ¿Hay algo que pueda hacer para mejorar el tiempo de reacción del selector cuando giro un solo o muy algunos mensajes? 150 micros no se ve bien. ¿O debería suponer que en un entorno prod el selector no será del todo?


Al hacer el giro activo alrededor de selectNow() Puedo obtener mejores resultados. Enviar pocos paquetes es aún peor que enviar muchos paquetes, pero creo que ahora estoy alcanzando el límite de rendimiento del selector. Mis resultados:

  • Envío de un paquete único Obtengo un tiempo de ida y vuelta de 65 micros consistente.
  • Enviando dos paquetes Obtengo alrededor de 39 micros el tiempo de ida y vuelta en promedio.
  • Enviando 10 paquetes Obtengo alrededor de 17 micros de ida y vuelta en promedio.
  • Envío de 10.000 paquetes Obtengo alrededor de 10.098 nanos en tiempo de ida y vuelta en promedio.
  • Enviando 1 millón de paquetes Obtengo 9,977 nanos en tiempo de ida y vuelta en promedio.

Conclusiones

  • Así que parece que la barrera física para la ida y vuelta de paquetes UDP es un promedio de 10 microsegundos, aunque tengo algunos paquetes que hacen el viaje en 8 micros (tiempo min) .

  • Con el giro ocupado (gracias Peter) pude pasar de 200 micros en promedio a un promedio de 65 micros en un solo paquete.

  • No estoy seguro de por qué ZeroMQ es 5 times slower que eso. (Editar: Tal vez porque estoy probando esta en la misma máquina a través de bucle de retorno y ZeroMQ está utilizando dos máquinas diferentes?)

+0

Creo que gran parte de esto se debe a los tiempos de calentamiento de HotSpot JVM en lugar del comportamiento de los selectores específicamente. – EJP

+1

Gracias @EJP, pero hice un poco de calentamiento con la JVM en modo de servidor. Envié un par de millones de mensajes antes de enviar los mensajes que activarán el punto de referencia. ¿Por qué crees que eso está pasando? = "Si juego con el envío de un solo mensaje, recibo varias veces entre 150 y 250 micros". – Julie

+0

llámame loco, pero ¿por qué no acabas de volver a implementar tu programa corto (en la descripción) en C y ver el rendimiento? – NoSenseEtAl

Respuesta

4

que suelen aparecer los casos no despertar un hilo puede ser muy costoso, no sólo porque toma tiempo para que la secuencia se active, pero el hilo se ejecuta 2-5 veces más lento durante decenas de microsegundos después como las cachés y

La forma en que he evitado esto en el pasado es esperar ocupado. Desafortunadamente, SelectNow crea una nueva colección cada vez que la llamas, incluso si se trata de una colección vacía. Esto genera tanta basura que no vale la pena usarla.

Una forma de evitar esto es esperar ocupado en enchufes sin bloqueo. Esto no se escala particularmente bien, pero puede brindarle la latencia más baja, ya que no es necesario que el subproceso se active y es más probable que el código que ejecute después esté en caché. Si también usa la afinidad de subprocesos, puede reducir la alteración de sus subprocesos.

Lo que también sugeriría es tratar de hacer que su código se bloquee menos y menos basura. Si hace esto, puede tener un proceso en Java que envíe una respuesta a un paquete entrante por debajo de 100 micro segundos el 90% del tiempo. Esto le permitirá procesar cada paquete a 100 Mb cuando lleguen (hasta 145 microsegundos de diferencia debido a las limitaciones de ancho de banda). Para una conexión de 1 Gb puede acercarse bastante.


Si desea comunicación entre procesos rápidos en la misma caja en Java, usted podría considerar algo así como https://github.com/peter-lawrey/Java-Chronicle Esta memoria compartida utiliza para transmitir mensajes con latencias de ida y vuelta (que es más difícil de hacer de manera eficiente con los zócalos) de menos de 200 nano segundos. También persiste en los datos y es útil si solo desea una forma rápida de producir un archivo de diario.

+0

Hola Peter. Por favor vea mis nuevos resultados basados ​​en sus comentarios. ¿Alguna idea de por qué ZeroMQ es 5 veces más lento que eso? – Julie

+0

ZeroMQ tiene que hacer más que simplemente enviar un paquete en un solo socket. Tiene que hacer más trabajo, enrutamiento, etc., por lo que su latencia será mayor. También sospecho que utiliza un hilo de fondo para hacer la recepción de envío, lo que mejora la capacidad de administración y el control de las conexiones (o al menos muchas de estas bibliotecas). Una de las ventajas que a menudo se ven es que al enviar mensajes por lotes utilizando un hilo de envío, aumentar el rendimiento 10 veces, que es lo que muchas bibliotecas se centran en lugar de latencia. –

+0

Sospecho que la diferencia se debe a que estoy probando esto a través de LOOPBACK. Estoy tratando de encontrar puntos de referencia ZeroMQ sobre loopback para comparar. ¿Un hilo de envío? ¡Eso es terrible! ¿Por qué no puedes simplemente llamar al canal de escritura y dejar que el sistema operativo haga el resto? Para baja latencia, cualquier cosa diferente a NIO es sin sentido en mi humilde opinión. – Julie

-1

Si sintoniza su selector a la derecha, puede obtener comunicación entre sockets en Java en menos de 2 microsegundos. Aquí están mis resultados de ida para un paquete UDP de 256 bytes:

Iterations: 1,000,000 
Message Size: 256 bytes 
Avg Time: 1,680 nanos 
Min Time: 1379 nanos 
Max Time: 7020 nanos 
75%: avg=1618 max=1782 nanos 
90%: avg=1653 max=1869 nanos 
99%: avg=1675 max=1964 nanos 
99.9%: avg=1678 max=2166 nanos 
99.99%: avg=1679 max=5094 nanos 
99.999%: avg=1680 max=5638 nanos 

hablo más sobre Java NIO y el patrón del reactor en mi artículo Inter-socket communication with less than 2 microseconds latency.

+4

Es una pena que el artículo en realidad no diga cómo lo hizo ... –

Cuestiones relacionadas