10
votes

Java TCP / Date de latence de la prise IP - coincé à 50 μs (microsecondes)? (utilisé pour Java IPC)

Nous profilons et nous profilions de notre application pour réduire le plus possible la latence. Notre application se compose de 3 processus Java distincts, tous fonctionnant sur le même serveur, qui transmettent des messages les uns aux autres sur des sockets TCP / IP.

Nous avons réduit le temps de traitement dans le premier composant à 25 μs, mais nous voyons que l'écriture de la prise TCP / IP (sur localhost) sur le composant suivant prend invariablement environ 50 μs. Nous voyons un autre comportement anormal, en ce que le composant accepter la connexion peut écrire plus rapidement (c'est-à-dire <50 μs). En ce moment, tous les composants fonctionnent <100 μs à l'exception des communications de socket.

Ne pas être un expert TCP / IP, je ne sais pas ce qui pourrait être fait pour accélérer cela. Les prises de domaine Unix seraient-elles plus rapides? MemoryPapsfiles? Quels autres mécanismes pourraient éventuellement être un moyen plus rapide de transmettre les données d'un processus Java à un autre?

Mise à jour 6/12/2011 Nous avons créé 2 applications de référence, une en Java et une en C ++ à la référence TCP / IP plus étroitement et à comparer. L'application Java a utilisé Nio (mode blocage) et la bibliothèque de TCP asio TCP. Les résultats étaient plus ou moins équivalents, avec l'application C ++ environ 4 μs plus rapidement que Java (mais dans l'un des tests Java Beat C ++). En outre, les deux versions ont montré beaucoup de variabilité dans le temps par message.

Je pense que nous sommes d'accord avec la conclusion de base selon laquelle une mise en œuvre de la mémoire partagée sera la plus rapide. (Bien que nous souhaitions également évaluer le produit Informatica, à condition que cela correspond au budget.)


8 commentaires

Le sténographie SI pour microsecondes est μs, pas μ (et vous devriez avoir un espace entre la quantité et l'unité). Je l'ai réparé pour vous.


Ne pas être un expert non plus, je suppose que UDP pourrait avoir vos latences, en raison d'être un protocole plus léger. Bien entendu, il est beaucoup plus douloureux de programmer, et pourrait ne pas généraliser aucun avantage si votre application doit mettre en œuvre manuellement la même fiabilité garantit que TCP fournit une boîte hors-tête.


Que diriez-vous de STDIN / STDOUT / STDERR (par exemple, le premier processus commence les 2 autres, et les communications ne se produisent qu'entre ce «maître» et les 2 esclaves)? Est-ce une option?


Y a-t-il une raison pour laquelle ces trois processus ne peuvent pas partager le même JVM?


@Marcelo: merci! Les pointeurs pour une meilleure grammaire et la syntaxe sont toujours appréciés!


Bonjour Olaf: Nous avons modifié les applications pour leur permettre d'être configurées pour fonctionner dans la même JVM. Mais en raison de leurs fonctions très différentes, il est souvent bénéfique de les avoir comme des processus distincts pour leur permettre d'être arrêté et redémarré à des moments différents, ce qui est utile pour plusieurs scénarios. De plus, nous avons eu une certaine expérience des retards de collecte des ordures étant amplifié dans l'un des composants plus rapides lorsqu'il est exécuté dans la même pièce JVM avec un autre composant moins efficace. Il existe donc une préoccupation (légèrement irrationnelle) concernant l'augmentation de la latence de GC en les combinant ensemble.


Juste pour vérifier - vous avez éteint NAGLE?


Je ne sais pas si ça compte toujours, mais voyez ce post .. Stackoverflow.com/Questtions/15725711/...


5 Réponses :


4
votes

Si vous utilisez des bibliothèques indigènes via JNI est une option, je envisagerais d'implémenter IPC comme d'habitude (recherche sur IPC , mmap, shm_open, etc.).

Il y a beaucoup de frais généraux associés à l'utilisation de JNI, mais au moins, il est un peu moins que les appels système complets nécessaires pour faire quoi que ce soit avec des sockets ou des tuyaux. Vous serez probablement en mesure de descendre à environ 3 microsecondes la latence à sens unique en utilisant une mise en œuvre de la mémoire partagée partagée via JNI. (Assurez-vous d'utiliser l'option -xcompan JVM ou d'ajuster le seuil de compilation, sinon vos 10 000 premiers échantillons seront terribles. Cela fait une grande différence.)

Je suis un peu surpris qu'un socket TCP écrit prend 50 microsecondes - la plupart des systèmes d'exploitation optimisent dans une certaine mesure la boucle TCP. Solaris fait en fait un très bon travail avec quelque chose appelé Fusion TCP . Et s'il y a a été une optimisation pour la communication de bouclage, elle est généralement pour TCP. UDP a tendance à être négligé - donc je ne me dérangerais pas dans ce cas. Je ne me dérangerais pas non plus avec des pipes (stdin / stdout ou vos propres tuyaux nommés, etc.), car ils vont être encore plus lents.

et généralement, beaucoup de la latence que vous voyez provient probablement de la signalisation - en attente d'un sélecteur IO comme Select () dans le cas de sockets ou d'attendre sur quelque chose. Si vous voulez la latence la plus basse possible, vous devrez graver un noyau assise dans un interrogation en boucle serré pour de nouvelles données.

Bien sûr, il y a toujours le Commercial Off-the-étagère itinéraire - que je connais pour une certitude Sercerait votre problème à la hâte - mais bien sûr, cela coûte de l'argent. Et dans l'intérêt de la divulgation complète: je travaille pour Informatica sur leur logiciel de messagerie à faible latence. (Et mon opinion honnête, en tant qu'ingénieur, c'est que c'est un logiciel assez fantastique - il convient de la peine de vérifier pour ce projet.)


2 commentaires

J'ai vérifié votre site Web et j'ai vu votre produit de messagerie ultra. J'ai vu que sur Cisco UCS, il montre une latence inférieure à 1 μs. Que pensez-vous que ce serait sur un serveur Linux standard? (E.G. 2 Dual Core Intel Xeon)?


J'ai réellement testé ici avec une machine Quad Q6600 Quad Q6600 Q6600, j'ai acheté environ 1 400 $, et je peux obtenir moins de 1 microseconde à ce sujet en C, aussi (juste un peu moins, cependant - ce n'est toujours pas aussi impressionnant que l'amateur Cisco Server Machine pourrait faire). Les numéros de microsecondes inférieurs sont tous à partir de points de repère exécutés avec des applications CLAIR C; Pour Java, ajoutez quelques microsecondes comme étage de base en raison de la surcharge de JNI. Et c'est aussi avec un fil de réception qui sonne dans une boucle serrée; Vous pouvez également exécuter non-sondage aussi, mais vous obtenez alors quelques autres munics de la latence de réveil de signalisation / fil de fil.




1
votes

MemoryMappilesFiles n'est pas une solution viable pour la faible latence IPC du tout - si le segment de mémoire mappé de la mémoire est mis à jour, il sera finalement synchronisé sur le disque, introduisant ainsi un délai imprévisible qui mesure au moins des mesures en millisecondes. Pour une latence faible, on peut essayer des combinaisons de la mémoire partagée + files d'attente de messages (notifications) ou de mémoire partagée + sémaphores. Cela fonctionne sur tous les UNIXES en particulier la version System V (non POSIX), mais si vous exécutez une application sur Linux, vous êtes plutôt sûr avec POSIX IPC (la plupart des fonctionnalités sont disponibles en 2,6 noyau) Oui, vous aurez besoin de JNI pour que cela soit fait.

upd upd: j'ai oublié que ceci est JVM - JVM IPC et nous avons déjà des GCS que nous ne pouvons pas contrôler pleinement, introduisant ainsi plusieurs pauses de MS parmi les tampons de fichier de système d'exploitation, Flash Flash sur disque peut être acceptable.


2 commentaires

"Si le segment de mémoire mappé est mis à jour, il sera finalement synchronisé sur le disque". Est-il seulement paginé sur le disque comme un "swap" (c'est-à-dire quand o / s n'a pas assez de bélier physique pour le garder uniquement en mémoire)?


PDFlush sait quand. sur Linux.



2
votes

"Le livre O'Reilly sur Nio (Java Nio, page 84), semble être vague sur si la cartographie de la mémoire reste en mémoire. Peut-être que c'est juste dire c'est comme autre mémoire, si vous manquez de physique, cela est échangé retour au disque, mais sinon pas? "

Linux. MMAP () Call Allocat Les pages dans la zone de cache de la page OS (qui sont périodiquement rougies sur le disque et peuvent être expulsées sur la base de l'approximation de l'algorithme de LRU?) La réponse à votre question est donc - Oui. La mémoire tampon mappée de mémoire peut être expulsée (en théorie) de la mémoire sauf si elle est Mlockke'd ( Mlock () ). Ceci est en théorie. En pratique, je pense qu'il est difficilement possible si votre système n'est pas un échange dans ce cas, les premières victimes sont des tampons de page.


0 commentaires

1
votes

Consultez https://github.com/pcdv/jockt

C'est un remplacement de la latence faible pour les sockets Java locaux qui utilisent la mémoire partagée.

La latence RTT entre 2 processus est bien inférieure à 1us sur une CPU moderne.


3 commentaires

J'ai commencé à regarder Jocket. Il semble utiliser mappébytebuffer qui a été suggéré par plusieurs autres réponses Heres. J'ai testé à l'aide de mappebytebuffer et je l'ai vu être très rapide pour IPC. Mais j'ai toujours la question sans réponse sur lorsque les E / S du disque ont lieu (qui introduit de très grandes pauses, en fonction de la taille du tampon rincé sur le disque).


C'est vrai, il utilise mappébytebuffer. J'ai également été inquiet par la latence d'E / S alors j'ai décidé de créer les fichiers sous "/ dev / shm" lorsque cela est possible (sous Linux, il est monté en tant que TMPFS afin qu'il n'y ait pas d'E / S). Cependant, dans mes repères, je n'ai pas pu observer aucune différence notable de la performance ...


Merci pour la pointe sur / dev / shm. Cela vaut vraiment la peine d'essayer.