web-dev-qa-db-fra.com

méthode la plus rapide (faible latence) pour la communication inter-processus entre Java et C / C ++

J'ai une application Java, qui se connecte via TCP à un "serveur" développé en C/C++.

l'application et le serveur s'exécutent sur la même machine, une boîte Solaris (mais nous envisageons de migrer vers Linux éventuellement). le type de données échangées est de simples messages (connexion, connexion ACK, puis le client demande quelque chose, réponses du serveur). chaque message fait environ 300 octets.

Actuellement, nous utilisons Sockets, et tout va bien, mais je cherche un moyen plus rapide d'échanger des données (latence plus faible), en utilisant les méthodes IPC.

J'ai fait des recherches sur le net et j'ai trouvé des références aux technologies suivantes:

  • la memoire partagée
  • tuyaux
  • files d'attente
  • ainsi que ce qui est appelé DMA (accès direct à la mémoire)

mais je n'ai pas pu trouver d'analyse appropriée de leurs performances respectives, ni comment les implémenter dans les deux Java et C/C++ (afin qu'ils puissent se parler), sauf peut-être les tuyaux que je pourrait imaginer comment faire.

quelqu'un peut-il commenter les performances et la faisabilité de chaque méthode dans ce contexte? un pointeur/lien vers des informations de mise en œuvre utiles?


MODIFIER/METTRE À JOUR

suite aux commentaires et réponses que j'ai reçus ici, j'ai trouvé des informations sur les sockets de domaine Unix, qui semblent être construits juste sur des canaux, et me feraient gagner la totalité TCP pile. c'est spécifique à la plate-forme, donc je prévoyez de le tester avec JNI ou juds ou junixsocket .

les prochaines étapes possibles seraient l'implémentation directe des tuyaux, puis la mémoire partagée, même si j'ai été averti du niveau supplémentaire de complexité ...


merci de votre aide

98
Bastien

Latence juste testée de Java sur mon Corei5 2,8 GHz, envoi/réception sur un seul octet seulement, 2 Java viennent d'être générés, sans affecter de cœurs de processeur spécifiques avec l'ensemble de tâches:

TCP         - 25 microseconds
Named pipes - 15 microseconds

Spécifie maintenant explicitement les masques de base, comme ensemble de tâches 1 Java Srv ou ensemble de tâches 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 !!!!

alors

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

Dans le même temps, Thread.sleep (0) (qui, comme le montre strace, provoque l'exécution d'un appel au noyau Linux sched_yield ()) prend 0,3 microseconde - les canaux nommés planifiés sur un seul cœur ont encore beaucoup de surcharge

Quelques mesures de mémoire partagée: 14 septembre 2009 - Solace Systems a annoncé aujourd'hui que son API de plate-forme de messagerie unifiée peut atteindre une latence moyenne de moins de 700 nanosecondes en utilisant un transport de mémoire partagée.http : //solacesystems.com/news/fastest-ipc-messaging/

P.S. - essayé la mémoire partagée le lendemain sous forme de fichiers mappés en mémoire, si une attente occupée est acceptable, nous pouvons réduire la latence à 0,3 microseconde pour passer un seul octet avec du code comme celui-ci:

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
}

Notes: Thread.sleep (0) est nécessaire pour que 2 processus puissent voir les changements de l'autre (je ne connais pas d'autre moyen encore). Si 2 processus forcés au même cœur avec l'ensemble de tâches, la latence devient 1,5 microsecondes - c'est un délai de changement de contexte

P.P.S - et 0,3 microseconde est un bon chiffre! Le code suivant prend exactement 0,1 microseconde, tout en effectuant une concaténation de chaîne primitive uniquement:

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

P.P.P.S - j'espère que ce n'est pas trop hors sujet, mais j'ai finalement essayé de remplacer Thread.sleep (0) avec l'incrémentation d'une variable int volatile statique (la JVM arrive à vider les caches CPU lors de cette opération) et j'ai obtenu - enregistrez! - Communication de processus Java à Java à latence de 72 nanosecondes !

Cependant, lorsqu'ils sont forcés sur le même cœur de processeur, les JVM à incrémentation volatile ne se contrôlent jamais, produisant ainsi exactement une latence de 10 millisecondes - Le quantum de temps Linux semble être de 5 ms ... Donc, cela ne devrait être utilisé que s'il y a un cœur de rechange - sinon le sommeil (0) est plus sûr.

101
Andriy

Le DMA est une méthode par laquelle les périphériques matériels peuvent accéder à la physique RAM sans interrompre le CPU. Par exemple, un contrôleur de disque dur peut copier des octets directement du disque vers la RAM. En tant que tel, il ne s'applique pas aux IPC.

La mémoire partagée et les canaux sont tous deux pris en charge directement par les systèmes d'exploitation modernes. En tant que tels, ils sont assez rapides. Les files d'attente sont généralement des abstractions, par exemple implémenté au dessus des sockets, des tuyaux et/ou de la mémoire partagée. Cela peut ressembler à un mécanisme plus lent, mais l'alternative est que vous crée une telle abstraction.

10
MSalters

La question a été posée il y a quelque temps, mais vous pourriez être intéressé par https://github.com/peter-lawrey/Java-Chronicle qui prend en charge des latences typiques de 200 ns et des débits de 20 M messages/seconde. Il utilise des fichiers mappés en mémoire partagés entre les processus (il conserve également les données, ce qui en fait le moyen le plus rapide de conserver les données)

10
Peter Lawrey

Voici un projet contenant des tests de performances pour divers transports IPC:

http://github.com/rigtorp/ipc-bench

7
sustrik

Une arrivée tardive, mais je voulais souligner un projet open source dédié à la mesure de la latence ping en utilisant Java NIO.

Plus exploré/expliqué dans ce article de blog . Les résultats sont (RTT en 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

C'est dans le sens de la réponse acceptée. L'erreur System.nanotime () (estimée en ne mesurant rien) est mesurée à environ 40 nanos, donc pour le IPC le résultat réel peut être inférieur.

6
Nitsan Wakart

Si vous envisagez d'utiliser un accès natif (puisque votre application et le "serveur" sont sur la même machine), pensez à JNA , il a moins de code passe-partout pour vous traiter.

6
bakkal

Je ne sais pas grand chose sur la communication inter-processus native, mais je suppose que vous devez communiquer en utilisant du code natif, auquel vous pouvez accéder en utilisant les mécanismes JNI. Donc, à partir de Java vous appelleriez une fonction native qui parle à l'autre processus.

2
fish

Dans mon ancienne entreprise, nous travaillions avec ce projet, http://remotetea.sourceforge.net/ , très facile à comprendre et à intégrer.

1
Seffi

Avez-vous envisagé de laisser les prises ouvertes pour que les connexions puissent être réutilisées?