metodo più veloce (bassa latenza) per la comunicazione tra processi tra Java e C / C ++


100

Ho un'app Java, che mi connette tramite socket TCP ad un "server" sviluppato in C / C ++.

sia l'app che il server sono in esecuzione sulla stessa macchina, una scatola Solaris (ma alla fine stiamo considerando la migrazione a Linux). il tipo di dati scambiati sono messaggi semplici (login, login ACK, poi il client chiede qualcosa, il server risponde). ogni messaggio è lungo circa 300 byte.

Attualmente stiamo usando i socket e tutto va bene, tuttavia sto cercando un modo più veloce per scambiare dati (latenza inferiore), usando i metodi IPC.

Ho fatto ricerche in rete e ho trovato riferimenti alle seguenti tecnologie:

  • memoria condivisa
  • tubi
  • code
  • così come quello che viene chiamato DMA (Direct Memory Access)

ma non sono riuscito a trovare un'analisi adeguata delle loro rispettive prestazioni, né come implementarle sia in JAVA che in C / C ++ (in modo che possano parlare tra loro), tranne forse pipe che potrei immaginare come fare.

qualcuno può commentare le prestazioni e la fattibilità di ciascun metodo in questo contesto? qualsiasi puntatore / collegamento a informazioni utili sull'implementazione?


MODIFICA / AGGIORNAMENTO

seguendo il commento e le risposte che ho ricevuto qui, ho trovato informazioni sui socket di dominio Unix, che sembrano essere costruiti appena su pipe, e mi farebbero risparmiare l'intero stack TCP. è specifico della piattaforma, quindi ho intenzione di testarlo con JNI o juds o junixsocket .

i prossimi passi possibili sarebbero l'implementazione diretta delle pipe, quindi la memoria condivisa, anche se sono stato avvertito del livello extra di complessità ...


Grazie per l'aiuto


7
Potrebbe essere eccessivo nel tuo caso, ma considera zeromq.org
jfs

è interessante, tuttavia l'idea sarebbe quella di utilizzare prima i metodi "generici" (come quelli forniti dal sistema operativo o forniti dal linguaggio), ecco perché ho menzionato le code e la memoria condivisa.
Bastien


Non dimenticare i file mappati o solo UDP.

10
UDP più lento di TCP ??? hmmm ... prova per favore
Boppity Bop

Risposte:


103

Ho appena testato la latenza da Java sul mio Corei5 2.8GHz, solo invio / ricezione di un singolo byte, 2 processi Java appena generati, senza assegnare core CPU specifici con il set di attività:

TCP         - 25 microseconds
Named pipes - 15 microseconds

Ora specificando esplicitamente le maschere principali, come taskset 1 java Srv o taskset 2 java Cli :

TCP, same cores:                      30 microseconds
TCP, explicit different cores:        22 microseconds
Named pipes, same core:               4-5 microseconds !!!!
Named pipes, taskset different cores: 7-8 microseconds !!!!

così

TCP overhead is visible
scheduling overhead (or core caches?) is also the culprit

Allo stesso tempo Thread.sleep (0) (che, come mostra strace, causa l'esecuzione di una singola chiamata al kernel di sched_yield ()) impiega 0,3 microsecondi - quindi i pipe programmati su single core hanno ancora molto overhead

Alcune misurazioni della memoria condivisa: 14 settembre 2009 - Solace Systems ha annunciato oggi che la sua API della piattaforma di messaggistica unificata può raggiungere una latenza media inferiore a 700 nanosecondi utilizzando un trasporto di memoria condivisa. http://solacesystems.com/news/fastest-ipc-messaging/

PS: ho provato la memoria condivisa il giorno successivo sotto forma di file mappati in memoria, se un'attesa impegnativa è accettabile, possiamo ridurre la latenza a 0,3 microsecondi per il passaggio di un singolo byte con codice come questo:

MappedByteBuffer mem =
  new RandomAccessFile("/tmp/mapped.txt", "rw").getChannel()
  .map(FileChannel.MapMode.READ_WRITE, 0, 1);

while(true){
  while(mem.get(0)!=5) Thread.sleep(0); // waiting for client request
  mem.put(0, (byte)10); // sending the reply
}

Note: Thread.sleep (0) è necessario in modo che 2 processi possano vedere le modifiche reciproche (non conosco ancora un altro modo). Se 2 processi vengono forzati sullo stesso core con il set di attività, la latenza diventa 1,5 microsecondi: è un ritardo del cambio di contesto

PPS - e 0,3 microsecondi è un buon numero! Il codice seguente richiede esattamente 0,1 microsecondi, mentre esegue solo una concatenazione di stringhe primitive:

int j=123456789;
String ret = "my-record-key-" + j  + "-in-db";

PPPS - spero che questo non sia troppo off-topic, ma alla fine ho provato a sostituire Thread.sleep (0) con l'incremento di una variabile int volatile statica (JVM accade per svuotare le cache della CPU quando lo fa) e ho ottenuto - record! - Comunicazione di processo da Java a Java con latenza di 72 nanosecondi !

Quando vengono forzate sullo stesso core della CPU, tuttavia, le JVM a incremento volatile non si controllano mai a vicenda, producendo così esattamente 10 millisecondi di latenza - il quantum temporale di Linux sembra essere 5 ms ... Quindi questo dovrebbe essere usato solo se c'è un core di riserva - altrimenti sleep (0) è più sicuro.


grazie Andriy, molto studio delle informazioni, e corrisponde più o meno alle mie misurazioni per TCP, quindi è un buon riferimento. Immagino che esaminerò le pipe con nome.
Bastien

Quindi la sostituzione del thread (Sleep) con l'incremento dell'intensità statica volatile dovrebbe essere eseguita solo se è possibile bloccare un processo su core diversi? Inoltre, non mi ero reso conto che potevi farlo? Ho pensato che il sistema operativo decide?
mezamorfico

3
Prova LockSupport.parkNanos (1), dovrebbe fare la stessa cosa.
reccles

Molto bella. Puoi fare di meglio (come nella latenza RTT 5-7us) per il ping TCP. Vedi qui: psy-lob-saw.blogspot.com/2012/12/…
Nitsan Wakart

1
Ulteriore esplorazione dell'utilizzo di file mappati in memoria come memoria condivisa per supportare la coda IPC in Java: psy-lob-saw.blogspot.com/2013/04/lock-free-ipc-queue.html raggiungendo 135 milioni di messaggi al secondo. Vedi anche la mia risposta di seguito per uno studio comparativo della latenza per metodo.
Nitsan Wakart

10

DMA è un metodo mediante il quale i dispositivi hardware possono accedere alla RAM fisica senza interrompere la CPU. Ad esempio, un esempio comune è un controller del disco rigido che può copiare byte direttamente dal disco alla RAM. In quanto tale, non è applicabile a IPC.

La memoria condivisa e le pipe sono entrambe supportate direttamente dai sistemi operativi moderni. In quanto tali, sono abbastanza veloci. Le code sono tipicamente astrazioni, ad esempio implementate sopra socket, pipe e / o memoria condivisa. Questo può sembrare un meccanismo più lento, ma l'alternativa è che si crea una tale astrazione.


per DMA, perché è così che posso leggere molte cose relative a RDMA (come Remote Direct Memory Access) che si applicherebbero attraverso la rete (specialmente con InfiniBand) e fare la stessa cosa. In realtà sto cercando di ottenere l'equivalente SENZA la rete (poiché tutto è sulla stessa scatola).
Bastien

RDMA ha lo stesso concetto: copiare byte su una rete senza interrompere le CPU su entrambi i lati. Ancora non funziona a livello di processo.
MSalters

10

La domanda è stata posta qualche tempo fa, ma potresti essere interessato a https://github.com/peter-lawrey/Java-Chronicle che supporta latenze tipiche di 200 ns e velocità di trasmissione di 20 M messaggi / secondo. Utilizza file mappati in memoria condivisi tra i processi (persiste anche i dati che lo rendono il modo più veloce per persistere i dati)



6

Se hai mai pensato di utilizzare l'accesso nativo (poiché sia ​​la tua applicazione che il "server" sono sulla stessa macchina), considera JNA , ha meno codice standard da gestire.


6

Un arrivo in ritardo, ma ha voluto segnalare un progetto open source dedicato alla misurazione della latenza del ping utilizzando Java NIO.

Ulteriori esplorazioni / spiegazioni in questo post del blog . I risultati sono (RTT in nanos):

Implementation, Min,   50%,   90%,   99%,   99.9%, 99.99%,Max
IPC busy-spin,  89,    127,   168,   3326,  6501,  11555, 25131
UDP busy-spin,  4597,  5224,  5391,  5958,  8466,  10918, 18396
TCP busy-spin,  6244,  6784,  7475,  8697,  11070, 16791, 27265
TCP select-now, 8858,  9617,  9845,  12173, 13845, 19417, 26171
TCP block,      10696, 13103, 13299, 14428, 15629, 20373, 32149
TCP select,     13425, 15426, 15743, 18035, 20719, 24793, 37877

Questo è sulla falsariga della risposta accettata. L'errore System.nanotime () (stimato non misurando nulla) viene misurato a circa 40 nanos quindi per l'IPC il risultato effettivo potrebbe essere inferiore. Godere.


2

Non so molto sulla comunicazione tra processi nativa, ma immagino che sia necessario comunicare utilizzando codice nativo, a cui è possibile accedere utilizzando meccanismi JNI. Quindi, da Java chiameresti una funzione nativa che dialoga con l'altro processo.



0

Hai considerato di tenere aperte le prese, in modo che le connessioni possano essere riutilizzate?


le prese rimangono aperte. la connessione è attiva per tutto il tempo di esecuzione dell'applicazione (circa 7 ore). i messaggi vengono scambiati più o meno continuamente (diciamo da 5 a 10 al secondo). la latenza attuale è di circa 200 microsecondi, l'obiettivo è di radere 1 o 2 ordini di grandezza.
Bastien

Una latenza di 2 ms? Ambizioso. Sarebbe possibile riscrivere il materiale C in una libreria condivisa a cui puoi interfacciarti usando JNI?
Thorbjørn Ravn Andersen

2ms è 2000 microsecondi, non 200. Questo rende 2ms molto meno ambiziosi.
thewhiteambit

-1

Segnalazione di bug Oracle sulle prestazioni di JNI: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4096069

JNI è un'interfaccia lenta e quindi i socket TCP Java sono il metodo più veloce per la notifica tra le applicazioni, tuttavia ciò non significa che devi inviare il payload su un socket. Usa LDMA per trasferire il payload, ma come hanno sottolineato le domande precedenti , il supporto Java per la mappatura della memoria non è l'ideale e quindi vorrai implementare una libreria JNI per eseguire mmap.


3
Perché JNI è lento? Considera come funziona il livello TCP di basso livello in Java, non è scritto in byte-code Java! (Ad esempio, questo deve passare attraverso l'host nativo.) Pertanto, rifiuto l'affermazione secondo cui i socket TCP Java sono più veloci di JNI. (JNI, tuttavia, non è IPC.)

4
Una singola chiamata JNI ti costa 9ns (su un Intel i5) se usi solo primitive. Quindi non è così lento.
Martin Kersten
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.