méthode la plus rapide (faible latence) pour la Communication entre Java et C / c++

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

les deux app & server tournent sur la même machine, une Solaris (mais nous envisageons éventuellement de migrer vers Linux). le type de données échangées est des messages simples (login, Login ACK, puis le client demande quelque chose, réponses du serveur). chaque message est d'environ 300 octets.

Actuellement nous utilisons des Sockets, et tout va bien, cependant je suis à la recherche d'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:

  • mémoire partagée
  • tuyaux
  • files d'attente
  • ainsi que ce qu'on appelle DMA (accès direct à la mémoire)

mais je n'ai pas pu trouver une analyse correcte de leurs performances respectives, ni comment les implémenter en JAVA et C / C++ (pour qu'ils puissent se parler), sauf peut-être des pipes que je pourrais imaginer comment faire.

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


MODIFIER / METTRE À JOUR

suite aux commentaires et réponses que j'ai eu ici, j'ai trouvé des informations sur les Sockets de domaine Unix, qui semblent être construits juste au-dessus des tuyaux, et me sauverait toute la pile TCP. il est spécifique à la plateforme, donc je prévois de le tester avec JNI ou soit juds ou junixsocket .

les prochaines étapes possibles seraient la mise en œuvre directe de pipes, puis la mémoire partagée, bien que j'ai été averti du niveau supplémentaire de complexité...


merci pour votre aide

92
demandé sur Bastien 2010-04-14 10:23:02

10 réponses

vient de tester la latence de Java sur mon Corei5 2,8 GHz, seul byte envoyer / reçu, 2 processus Java viennent d'apparaître, sans affecter de cœurs CPU spécifiques avec taskset:

TCP         - 25 microseconds
Named pipes - 15 microseconds

spécifiant désormais explicitement les masques core, comme taskset 1 java Srv ou 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 !!!!

donc

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

en même temps fil.sleep(0) (qui, comme strace montre causes d'un seul sched_yield() du noyau Linux appel à être exécuté) prend 0.3 microseconde - ainsi nommé tuyaux prévue à simple cœur ont encore beaucoup de frais généraux

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

P.S.-j'ai essayé la mémoire partagée le lendemain sous forme de fichiers de mappage de mémoire, si l'attente occupée est acceptable, nous pouvons réduire la latence à 0,3 microseconde pour passer un seul octet avec un 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 pour le moment). Si 2 processus sont forcés au même noyau avec taskset, le la latence devient 1.5 microsecondes - c'est un délai de commutation de contexte

P. P. S et 0,3 microseconde est un bon nombre! Le code suivant prend exactement 0.1 microseconde, tout en faisant une concaténation de chaîne primitive seulement:

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

P. P. P. S - espérons que ce n'est pas trop hors-sujet, mais finalement, j'ai essayé de remplacer le Fil de discussion.sleep(0) avec incrementing une variable int statique volatile (JVM arrive à rincer les caches CPU en faisant ainsi) et obtained - record! - 72 nanosecondes latence java-java processus de communication !

quand ils sont forcés au même noyau CPU, cependant, les JVM à incrémentation volatile ne se donnent jamais le contrôle l'un de l'autre, produisant ainsi exactement 10 millisecondes de latence-le quantum de temps Linux semble être de 5ms... Ceci ne doit donc être utilisé que s'il y a un noyau de rechange - sinon sleep(0) est plus sûr.

98
répondu Andriy 2011-06-21 13:31:54

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 supporte des latences typiques de 200 ns et des débits de messages de 20 m/seconde. Il utilise des fichiers cartographiés en mémoire partagés entre les processus (il persiste également les données qui le rend le moyen le plus rapide de persister des données)

10
répondu Peter Lawrey 2012-07-15 06:48:56

DMA est une méthode par laquelle les périphériques matériels peuvent accéder à la RAM physique sans interrompre le CPU. Par exemple: un exemple courant est un contrôleur de disque dur qui peut copier des octets directement du disque à la mémoire vive. En tant que tel, il n'est pas applicable à IPC.

mémoire partagée et pipes sont tous les deux pris en charge directement par OSes modernes. En tant que tels, ils sont assez rapide. Les files d'attente sont typiquement des abstractions, par exemple implémentées sur des sockets, des pipes et/ou de la mémoire partagée. Cela peut ressembler à un plus lent mécanisme, mais l'alternative est que vous créer une telle abstraction.

8
répondu MSalters 2010-04-14 08:58:10

voici un projet contenant des tests de performance pour divers transports IPC:

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

7
répondu sustrik 2010-04-15 04:54:00

si vous envisagez un jour d'utiliser l'accès natif (puisque votre application et le" serveur "sont sur la même machine), considérez JNA , il a moins de code boilerplate pour vous traiter.

6
répondu bakkal 2010-04-14 07:15:56

une arrivée tardive, mais a voulu signaler un open source project dédié à la mesure de la latence ping en utilisant Java NIO.

exploré/expliqué plus en détail dans ce post de blog . Les résultats sont (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

dans le sens de la réponse acceptée. Système.l'erreur nanotime () (estimée en ne mesurant rien) est mesurée à environ 40 nanos donc pour la CIB le résultat réel peut être inférieure. Profiter.

6
répondu Nitsan Wakart 2013-07-03 14:08:12

Je ne sais pas grand-chose sur la communication native entre les processus, mais je suppose que vous devez communiquer en utilisant le 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
répondu fish 2010-04-14 06:52:54

dans mon ancienne société, nous avons travaillé avec ce projet, http://remotetea.sourceforge.net / , très facile à comprendre et à intégrer.

1
répondu Seffi 2010-04-14 06:33:18

avez-vous envisagé de garder les sockets ouverts, pour que les connexions puissent être réutilisées?

0
répondu Thorbjørn Ravn Andersen 2010-04-15 05:22:44

Oracle bug report on JNI performance: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4096069

JNI est une interface lente et donc les sockets TCP Java sont la méthode la plus rapide pour la notification entre les applications, mais cela ne signifie pas que vous devez envoyer la charge utile sur une socket. Utilisez LDMA pour transférer la charge utile, mais comme questions précédentes ont souligné, Java support for memory mapping n'est pas idéal et vous voudrez donc mettre en œuvre une bibliothèque JNI pour exécuter mmap.

-1
répondu Steve-o 2017-05-23 12:18:17