Tenho um aplicativo Java, conectando-se através de soquete TCP a um "servidor" desenvolvido em C / C ++.
o aplicativo e o servidor estão rodando na mesma máquina, uma caixa Solaris (mas estamos considerando migrar para o Linux eventualmente). tipo de dados trocados são mensagens simples (login, login ACK, então o cliente pede algo, o servidor responde). cada mensagem tem cerca de 300 bytes.
Atualmente estamos usando Sockets, e está tudo bem, porém estou procurando uma forma mais rápida de trocar dados (menor latência), usando métodos IPC.
Tenho pesquisado na Internet e encontrei referências às seguintes tecnologias:
- memoria compartilhada
- tubos
- filas
- bem como o que é referido como DMA (Direct Memory Access)
mas não consegui encontrar uma análise adequada de seus respectivos desempenhos, nem como implementá-los em JAVA e C / C ++ (para que eles possam se comunicar), exceto talvez pipes que eu poderia imaginar como fazer.
Alguém pode comentar sobre o desempenho e a viabilidade de cada método neste contexto? qualquer indicador / link para informações úteis de implementação?
EDITAR / ATUALIZAR
seguindo o comentário e as respostas que obtive aqui, encontrei informações sobre os soquetes de domínio Unix, que parecem ser construídos sobre tubos, e me salvariam toda a pilha TCP. é específico da plataforma, então pretendo testá-lo com JNI ou juds ou junixsocket .
os próximos passos possíveis seriam a implementação direta de tubos e, em seguida, memória compartilhada, embora eu tenha sido avisado sobre o nível extra de complexidade ...
Obrigado pela ajuda
Respostas:
Acabei de testar a latência do Java no meu Corei5 de 2,8 GHz, envio / recebimento de apenas um byte, 2 processos Java recém-gerados, sem atribuir núcleos de CPU específicos ao conjunto de tarefas:
Agora especificando explicitamente as máscaras principais, como taskset 1 java Srv ou taskset 2 java Cli :
tão
Ao mesmo tempo, Thread.sleep (0) (que como mostra o strace faz com que uma única chamada de kernel do Linux sched_yield () seja executada) leva 0,3 microssegundo - assim, os pipes nomeados agendados para um único núcleo ainda têm muita sobrecarga
Algumas medidas de memória compartilhada: 14 de setembro de 2009 - Solace Systems anunciou hoje que sua API de plataforma de mensagem unificada pode atingir uma latência média de menos de 700 nanossegundos usando um transporte de memória compartilhada. http://solacesystems.com/news/fastest-ipc-messaging/
PS - tentei memória compartilhada no dia seguinte na forma de arquivos mapeados de memória, se a espera ocupada for aceitável, podemos reduzir a latência para 0,3 microssegundo para passar um único byte com código como este:
Notas: Thread.sleep (0) é necessário para que 2 processos possam ver as alterações um do outro (não conheço outra maneira ainda). Se 2 processos forçados ao mesmo núcleo com o conjunto de tarefas, a latência torna-se 1,5 microssegundos - isso é um atraso de mudança de contexto
PPS - e 0,3 microssegundo é um bom número! O código a seguir leva exatamente 0,1 microssegundo, ao fazer apenas uma concatenação de string primitiva:
PPPS - espero que isso não seja muito fora do assunto, mas finalmente tentei substituir Thread.sleep (0) com o incremento de uma variável int estática volátil (JVM acontece de liberar caches de CPU ao fazer isso) e obtido - registro! - Comunicação do processo java-para-java com latência de 72 nanossegundos !
Quando forçados ao mesmo núcleo de CPU, no entanto, JVMs de incremento volátil nunca fornecem controle um ao outro, produzindo assim exatamente 10 milissegundos de latência - o quantum de tempo do Linux parece ser 5 ms ... Portanto, deve ser usado apenas se houver um núcleo sobressalente - caso contrário, dormir (0) é mais seguro.
fonte
DMA é um método pelo qual os dispositivos de hardware podem acessar a RAM física sem interromper a CPU. Por exemplo, um exemplo comum é um controlador de disco rígido que pode copiar bytes direto do disco para a RAM. Como tal, não é aplicável ao IPC.
A memória compartilhada e os canais são suportados diretamente por sistemas operacionais modernos. Como tal, são bastante rápidos. Filas são normalmente abstrações, por exemplo, implementadas em cima de soquetes, tubos e / ou memória compartilhada. Isso pode parecer um mecanismo mais lento, mas a alternativa é que você crie essa abstração.
fonte
A pergunta foi feita há algum tempo, mas você pode estar interessado em https://github.com/peter-lawrey/Java-Chronicle que suporta latências típicas de 200 ns e taxas de transferência de 20 M mensagens / segundo. Ele usa arquivos mapeados de memória compartilhados entre processos (também persiste os dados, o que torna a maneira mais rápida de persistir os dados)
fonte
Este é um projeto que contém testes de desempenho para vários transportes IPC:
http://github.com/rigtorp/ipc-bench
fonte
Se você já pensou em usar o acesso nativo (já que seu aplicativo e o "servidor" estão na mesma máquina), considere JNA , ele tem menos código clichê para você lidar.
fonte
Chegou tarde, mas queria apontar um projeto de código aberto dedicado a medir a latência de ping usando Java NIO.
Mais explorado / explicado nesta postagem do blog . Os resultados são (RTT em nanos):
Isso está de acordo com as linhas da resposta aceita. O erro System.nanotime () (estimado sem medir nada) é medido em cerca de 40 nanos, portanto, para o IPC, o resultado real pode ser menor. Aproveitar.
fonte
Não sei muito sobre comunicação nativa entre processos, mas acho que você precisa se comunicar usando código nativo, que pode ser acessado usando mecanismos JNI. Portanto, em Java, você chamaria uma função nativa que se comunica com o outro processo.
fonte
Na minha antiga empresa trabalhávamos com este projeto, http://remotetea.sourceforge.net/ , muito fácil de entender e integrar.
fonte
Você já pensou em manter os soquetes abertos para que as conexões possam ser reutilizadas?
fonte
Relatório de bug da Oracle sobre o desempenho JNI: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4096069
JNI é uma interface lenta e, portanto, os soquetes Java TCP são o método mais rápido para notificação entre aplicativos; no entanto, isso não significa que você precisa enviar a carga útil por um soquete. Use LDMA para transferir a carga útil, mas, como as questões anteriores apontaram, o suporte Java para mapeamento de memória não é ideal e, portanto, você desejará implementar uma biblioteca JNI para executar o mmap.
fonte