Tengo una aplicación Java, que se conecta a través de un socket TCP a un "servidor" desarrollado en C / C ++.
Tanto la aplicación como el servidor se ejecutan en la misma máquina, una caja Solaris (pero estamos considerando migrar a Linux eventualmente). El tipo de datos intercambiados son mensajes simples (inicio de sesión, ACK de inicio de sesión, luego el cliente pide algo, el servidor responde). cada mensaje tiene una longitud aproximada de 300 bytes.
Actualmente estamos usando Sockets y todo está bien, sin embargo, estoy buscando una forma más rápida de intercambiar datos (menor latencia), usando métodos IPC.
He estado investigando en la red y encontré referencias a las siguientes tecnologías:
- memoria compartida
- tubería
- colas
- así como lo que se conoce como DMA (acceso directo a memoria)
pero no pude encontrar un análisis adecuado de sus respectivos rendimientos, ni cómo implementarlos tanto en JAVA como en C / C ++ (para que puedan hablar entre sí), excepto tal vez tuberías que podría imaginar cómo hacerlo.
¿Alguien puede comentar sobre el rendimiento y la viabilidad de cada método en este contexto? ¿Algún puntero / enlace a información de implementación útil?
EDITAR / ACTUALIZAR
Siguiendo el comentario y las respuestas que obtuve aquí, encontré información sobre Unix Domain Sockets, que parecen estar construidos solo sobre tuberías, y me ahorrarían toda la pila TCP. es específico de la plataforma, así que planeo probarlo con JNI o juds o junixsocket .
Los siguientes pasos posibles serían la implementación directa de tuberías, luego la memoria compartida, aunque me han advertido del nivel adicional de complejidad ...
gracias por tu ayuda
Respuestas:
Acabo de probar la latencia de Java en mi Corei5 2.8GHz, solo envío / recepción de un solo byte, 2 procesos Java recién generados, sin asignar núcleos de CPU específicos con el conjunto de tareas:
Ahora especifica explícitamente máscaras centrales, como taskset 1 java Srv o taskset 2 java Cli :
entonces
Al mismo tiempo, Thread.sleep (0) (que como muestra strace hace que se ejecute una única llamada al kernel de Linux sched_yield ()) tarda 0,3 microsegundos, por lo que las canalizaciones con nombre programadas para un solo núcleo todavía tienen mucha sobrecarga
Algunas medidas de memoria compartida: 14 de septiembre de 2009 - Solace Systems anunció hoy que su API de plataforma de mensajería unificada puede alcanzar una latencia promedio de menos de 700 nanosegundos utilizando un transporte de memoria compartida. http://solacesystems.com/news/fastest-ipc-messaging/
PD: probé la memoria compartida al día siguiente en forma de archivos mapeados en memoria, si la espera ocupada es aceptable, podemos reducir la latencia a 0.3 microsegundos para pasar un solo byte con un código como este:
Notas: Thread.sleep (0) es necesario para que 2 procesos puedan ver los cambios entre sí (todavía no conozco otra forma). Si 2 procesos se fuerzan al mismo núcleo con el conjunto de tareas, la latencia se convierte en 1,5 microsegundos, es un retraso de cambio de contexto
PPS: ¡y 0,3 microsegundos es un buen número! El siguiente código toma exactamente 0,1 microsegundos, mientras que solo realiza una concatenación de cadenas primitivas:
PPPS: espero que esto no sea demasiado fuera de tema, pero finalmente intenté reemplazar Thread.sleep (0) con el incremento de una variable int volátil estática (JVM descarga los cachés de la CPU al hacerlo) y obtuve: ¡registro! - 72 nanosegundos de latencia de comunicación de proceso de Java a Java .
Sin embargo, cuando se les obliga al mismo núcleo de CPU, las JVM de incremento volátil nunca ceden el control entre sí, por lo que producen una latencia de exactamente 10 milisegundos; el tiempo cuántico de Linux parece ser de 5 ms ... Por lo tanto, esto debe usarse solo si hay un núcleo de repuesto. de lo contrario, dormir (0) es más seguro.
fuente
DMA es un método mediante el cual los dispositivos de hardware pueden acceder a la RAM física sin interrumpir la CPU. Por ejemplo, un ejemplo común es un controlador de disco duro que puede copiar bytes directamente del disco a la RAM. Como tal, no es aplicable a IPC.
La memoria compartida y las canalizaciones son compatibles directamente con los sistemas operativos modernos. Como tal, son bastante rápidos. Las colas son típicamente abstracciones, por ejemplo, implementadas sobre sockets, tuberías y / o memoria compartida. Esto puede parecer como un mecanismo más lento, pero la alternativa es que se crea una abstracción.
fuente
La pregunta se hizo hace algún tiempo, pero es posible que le interese https://github.com/peter-lawrey/Java-Chronicle, que admite latencias típicas de 200 ns y rendimientos de 20 M mensajes / segundo. Utiliza archivos mapeados en memoria compartidos entre procesos (también conserva los datos, lo que hace que sea la forma más rápida de conservar los datos)
fuente
Aquí hay un proyecto que contiene pruebas de rendimiento para varios transportes IPC:
http://github.com/rigtorp/ipc-bench
fuente
Si alguna vez considera usar el acceso nativo (ya que tanto su aplicación como el "servidor" están en la misma máquina), considere JNA , tiene menos código repetitivo con el que lidiar.
fuente
Llegó tarde, pero quería señalar un proyecto de código abierto dedicado a medir la latencia del ping utilizando Java NIO.
Más explorado / explicado en esta publicación de blog . Los resultados son (RTT en nanos):
Esto está en la línea de la respuesta aceptada. El error System.nanotime () (estimado sin medir nada) se mide en alrededor de 40 nanos, por lo que para el IPC el resultado real podría ser menor. Disfrutar.
fuente
No sé mucho sobre la comunicación nativa entre procesos, pero supongo que necesita comunicarse usando código nativo, al que puede acceder usando mecanismos JNI. Entonces, desde Java, llamaría a una función nativa que habla con el otro proceso.
fuente
En mi antigua empresa solíamos trabajar con este proyecto, http://remotetea.sourceforge.net/ , muy fácil de entender e integrar.
fuente
¿Ha considerado mantener los enchufes abiertos para que las conexiones se puedan reutilizar?
fuente
Informe de errores de Oracle sobre el rendimiento de JNI: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4096069
JNI es una interfaz lenta y, por lo tanto, los sockets TCP de Java son el método más rápido para la notificación entre aplicaciones, sin embargo, eso no significa que tenga que enviar la carga útil a través de un socket. Use LDMA para transferir la carga útil, pero como han señalado las preguntas anteriores , el soporte de Java para el mapeo de memoria no es ideal y, por lo tanto, querrá implementar una biblioteca JNI para ejecutar mmap.
fuente