Pourquoi l'équipe de LMAX a-t-elle utilisé Java et conçu l'architecture pour éviter à tout prix le GC?

24

Pourquoi l'équipe de LMAX a-t-elle conçu le disjoncteur LMAX en Java alors que toutes leurs conceptions visent à minimiser l'utilisation du GC? Si l'on ne veut pas que GC s'exécute, alors pourquoi utiliser une langue récupérée?

Leurs optimisations, le niveau de connaissance du matériel et la pensée qu'ils mettent sont tout simplement géniaux mais pourquoi Java?

Je ne suis pas contre Java ou quoi que ce soit, mais pourquoi un langage GC? Pourquoi ne pas utiliser quelque chose comme D ou tout autre langage sans GC mais permet un code efficace? Est-ce que l'équipe est la plus familière avec Java ou Java possède-t-il un avantage unique que je ne vois pas?

Disons qu'ils le développent en utilisant D avec une gestion manuelle de la mémoire, quelle serait la différence? Ils devraient penser à un niveau bas (ce qu'ils sont déjà), mais ils peuvent retirer les meilleures performances du système car il est natif.


la source
6
Je connais très peu de choses sur ce projet, mais il semble que ce soit une sorte de cadre sur lequel d'autres peuvent s'appuyer. Et si vous parvenez à écrire cela en Java (et à permettre à d'autres de coder en Java et d'en récolter les bénéfices), alors vous aurez une "base de clients" BEAUCOUP plus importante que si vous l'aviez écrite en D.
Joachim Sauer
6
@kadaj: peu importe que le consommateur soit public ou interne: si vous le rendez accessible dans une langue largement connue, il sera plus utile, même pour le développement interne. Si vous commencez votre argument (hypothétique) par: "Supposons que tout le monde connaît D aussi bien que Java, ...", alors vous manquez probablement quelque chose.
Joachim Sauer
6
Certaines personnes aiment utiliser des marteaux pour toutes sortes de problèmes. Vous avez un bord rugueux que vous souhaitez raboter, frappez-le avec votre marteau jusqu'à ce qu'il soit lisse. Vous avez une vis que vous devez enfoncer, la frapper avec un marteau jusqu'à ce qu'elle soit en place. C ou C ++ aurait été un meilleur choix que D, ne serait-ce que pour la base de connaissances existante. Je ne sais pas pourquoi vous avez même cité D comme exemple TBH.
gbjbaanb
2
@gbjbaanb J'ai mentionné D parce qu'il fournit la collecte des ordures (dans les cas où des abstractions de haut niveau sont nécessaires et que manipuler la mémoire est trop difficile pour le cerveau) mais permet également une gestion manuelle de la mémoire avec malloc style C et gratuit. D est un peu comme Objective-C avec ARC (pas de vrai GC) mais mieux que ça. Mais oui, C / C ++ ferait l'affaire.
4
@kadaj Je vois que vous avez reçu des critiques pour avoir parlé de D, mais je tiens à dire que je suis déçu du ton que les autres utilisent et explique pourquoi je pense que D est au cœur de la question. Bien que D ne soit en effet pas largement utilisé, D fournit des constructions de haut niveau que je pourrais m'attendre à trouver dans Java ou C # mais pas dans (du moins à l'ancienne) C ++. Il prévoit toujours le mélange géré et non géré - qui est à peu près la seule langue que je connais pour le faire! Donc, D n'est pas seulement un choix pour animaux de compagnie, mais plutôt un objectif dont les objectifs coïncident avec les questions originales sur GC.
J Trana

Réponses:

20

Parce qu'il y a une énorme différence entre optimiser les performances et désactiver complètement une sécurité

En réduisant le nombre de GC, leur structure est plus réactive et peut fonctionner (vraisemblablement) plus rapidement. Maintenant, l'optimisation pour le ramasse-miettes ne signifie pas qu'ils ne font jamais de ramasse-miettes. Cela signifie simplement qu'ils le font moins souvent, et quand ils le font, cela fonctionne très rapidement. Ce type d'optimisation comprend:

  1. Minimiser le nombre d'objets qui se déplacent vers un espace survivant (c'est-à-dire qui ont survécu à au moins une collecte des ordures) en utilisant de petits objets jetables. Les objets qui se sont déplacés vers l'espace du survivant sont plus difficiles à collecter et une collecte de déchets ici implique parfois de geler toute la JVM.
  2. N'allouez pas trop d'objets pour commencer. Cela peut se retourner si vous ne faites pas attention, car les objets de la jeune génération sont super bon marché à allouer et à collecter.
  3. Assurez-vous que le nouvel objet pointe vers l'ancien (et non l'inverse) afin que le jeune objet soit facile à collecter, car il n'y a aucune référence à ceux-ci qui entraînera sa conservation.

Lorsque vous désactivez les performances, vous ajustez généralement un "point chaud" très spécifique tout en ignorant le code qui ne s'exécute pas souvent. Si vous faites cela en Java, vous pouvez laisser le garbage collector s'occuper de ces coins sombres (car cela ne fera pas beaucoup de différence) tout en optimisant très soigneusement la zone qui s'exécute en boucle serrée. Vous pouvez donc choisir où vous optimisez et où vous ne le faites pas, et vous pouvez ainsi concentrer vos efforts là où cela compte.


Maintenant, si vous désactivez complètement la récupération de place, vous ne pouvez pas choisir. Vous devez vous débarrasser manuellement de chaque objet, jamais. Cette méthode est appelée au plus une fois par jour? En Java, vous pouvez le laisser faire, car son impact sur les performances est négligeable (il peut être correct de laisser un GC complet se produire chaque mois). En C ++, vous perdez toujours des ressources, vous devez donc prendre soin même de cette méthode obscure. Vous devez donc payer le prix de la gestion des ressources dans chaque partie de votre application, tandis qu'en Java, vous pouvez vous concentrer.


Mais ça empire.

Et si vous avez un bug, disons dans un coin sombre de votre application qui n'est accessible que le lundi à la pleine lune? Java a une forte garantie de sécurité. Il y a peu ou pas de "comportement indéfini". Si vous utilisez quelque chose de mal, une exception est levée, votre programme s'arrête et aucune corruption de données ne se produit. Vous êtes donc presque sûr que rien de mal ne peut se produire sans que vous vous en rendiez compte.

Mais dans quelque chose comme D, vous pouvez avoir un mauvais accès au pointeur, ou un débordement de tampon, et vous pouvez corrompre votre mémoire, mais votre programme ne le saura pas (vous avez désactivé la sécurité, rappelez-vous?) Et continuera à fonctionner avec son incorrect données, et faire des choses assez désagréables et corrompre vos données, et vous ne savez pas, et comme plus de corruption se produit, vos données deviennent de plus en plus erronées, puis soudainement elles se cassent, et c'était dans une application vitale, et une erreur est survenue dans le calcul d'une fusée, et il ne fonctionne pas, et la fusée explose, et mourir quelqu'un, et votre entreprise est dans la première page de tous les journaux et votre patron point de son doigt pour vous dire : « vous êtes l'ingénieur qui a suggéré que nous utilisions D pour optimiser les performances, comment se fait- il que vous n'ayez pas pensé à la sécurité?". Et c'est de ta faute. Tu as tué ces gens avec ta folle tentative de performance.


OK, ok, la plupart du temps c'est beaucoup moins dramatique que ça. Mais même une application critique pour l'entreprise ou simplement une application GPS ou, disons, un site Web gouvernemental de santé peut avoir des conséquences assez négatives si vous avez des bugs. Utiliser un langage qui les empêche complètement ou qui échoue lorsqu'ils surviennent est généralement une très bonne idée.

Il y a un coût à désactiver une sécurité. Devenir natif n'a pas toujours de sens. Parfois, il est beaucoup plus simple et plus sûr d'optimiser un peu un langage sûr pour aller dans une langue où vous pouvez vous tirer une balle dans le pied. L'exactitude et la sécurité dans de nombreux cas l'emportent sur les quelques nano secondes que vous auriez supprimées en éliminant complètement le GC. Disruptor peut être utilisé dans ces situations, donc je pense que LMAX-Exchange a fait le bon appel.

Mais qu'en est-il de D en particulier? Vous avez un GC si vous le souhaitez pour les coins sombres, et le sous-ensemble SafeD (que je ne connaissais pas avant l'édition) supprime le comportement indéfini (si vous vous souvenez de l'utiliser!).

Eh bien, dans ce cas, c'est une simple question de maturité. L'écosystème Java est plein d'outils bien écrits et de bibliothèques matures (mieux pour le développement). Beaucoup plus de développeurs connaissent Java que D (mieux pour la maintenance). Choisir une langue nouvelle et moins populaire pour quelque chose d'aussi critique qu'une application financière n'aurait pas été une bonne idée. Avec un langage moins connu, si vous avez un problème, peu de gens peuvent vous aider, et les bibliothèques que vous trouvez ont tendance à avoir plus de bugs car elles ont été exposées à moins de personnes.

Donc, mon dernier point tient toujours: si vous voulez éviter des problèmes aux conséquences désastreuses, restez avec des choix sûrs. A ce stade de la vie de D, ses clients sont les petites start-ups prêtes à prendre des risques fous. Si un problème peut coûter des millions, il vaut mieux rester plus loin dans la courbe en cloche de l' innovation .

Laurent Bourgault-Roy
la source
2
Le message original appelle spécifiquement D. Il y a en fait une grande différence entre C ++ et D en ce qui concerne la granularité du choix. Même si vous choisissez d'aller en gestion complète dans le sous-ensemble SafeD, je pense que vous obtenez un peu plus de contrôle sur certains aspects de la collecte et du calendrier (activer / désactiver, collecter, minimiser). Découvrez les stratégies de Digital Mars pour la gestion de la mémoire!
J Trana
2
lmax évite délibérément une partie de la sécurité offerte par Java
James
Ce serait une excellente réponse, sauf que Java n'est pas sous licence pour les logiciels critiques. Si vous avez un réacteur anucléaire, il sera écrit en C ++ et non en Java, ce qui élimine un peu tout l'aspect "sécurité".
gbjbaanb
@gbjbaanb, [citation nécessaire]. Les normes / directives de fiabilité que j'ai vues recommandent d' abord d' éviter le C / C ++ au profit d'autres langages; et si vous y entrez, utilisez des versions très restreintes des langues (MISRA, etc.). Et une fois que vous acceptez les restrictions, je ne vois pas pourquoi vous ne pourriez pas faire de même avec une autre langue. Si vous pensiez à la mention par Java Licence de "pas pour les installations nucléaires" dans la section RESTRICTIONS, cela semble avoir changé il y a quelque temps et maintenant, à la place, cela dit simplement quelque chose qui s'apparente à "soyez prudent, pas notre responsabilité". Pourtant, je suppose que le (...)
hmijail
(...) le libellé d'origine était comme les licences de gcc et de clang: aucune garantie à des fins spécifiques. Donc, vous ne les utiliseriez pas pour quelque chose qui a besoin de fiabilité, et vous auriez plutôt besoin d'utiliser un compilateur certifié, si vous n'alliez pas jusqu'à un langage spécifique pour le travail (Ada?).
hmijail
4

Il semble que la raison pour laquelle il est écrit en Java est qu'ils possèdent une expertise Java en interne et il a probablement été écrit (bien qu'il soit encore en développement actif) avant que C ++ n'agisse avec C ++ 0x / 11.

Leur code n'est vraiment que Java par leur nom, ils utilisent un peu sun.misc.Unsafe, ce qui défait le point de Java et la sécurité est censée donner. J'ai écrit un port C ++ du Disruptor et il surpasse le code Java qu'ils livrent (je n'ai pas passé beaucoup de temps à régler la JVM).

Cela dit, les principes que le perturbateur suit ne sont pas spécifiques au langage, par exemple Ne vous attendez pas à un code C ++ à faible latence qui alloue ou libère du tas.

James
la source
Pouvez-vous indiquer votre implémentation? J'ai vu quelques réimplémentations de ce type prétendre à des performances supérieures, mais les deux ont triché avec des simplifications: par exemple, câbler 1 producteur + 1 consommateur au lieu d'être multi-producteur / consommateur capable comme le disruptor d'origine. L'auteur du disrupteur lui-même a mentionné dans un fil de discussion Google Groupes que les performances pourraient être améliorées par des paramètres de câblage dans la version Java.
hmijail
4

Cette question énonce une prémisse incorrecte comme fait, puis fait un argument à propos de cette prémisse incorrecte.

Permet de creuser dans cela .. "tous leurs points de conception pour minimiser l'utilisation du GC" - n'est tout simplement pas vrai. L'innovation dans le disrupteur a peu à voir avec GC. Le perturbateur fonctionne parce que sa conception considère intelligemment le fonctionnement des ordinateurs modernes - quelque chose de beaucoup moins courant que ce à quoi on pourrait s'attendre. Voir le discours de Cliff Click http://www.azulsystems.com/events/javaone_2009/session/2009_J1_HardwareCrashCourse.pdf pour une discussion.

Il est bien connu que LMax est client d'Azul. Je sais de première main qu'avec les GC Azul, il n'y a tout simplement pas de problème - même avec des tas de 175 Go.

peterbooth
la source
Il y a un grain de vérité à cela. Ils redémarrent la machine virtuelle tous les soirs pour éviter une collecte majeure. C'est ce que Martin Fowler a écrit, de toute façon et il n'est pas un mannequin: "Comme le reste du système, les perturbateurs sont rebondis du jour au lendemain. Ce rebond est principalement fait pour effacer la mémoire afin qu'il y ait moins de chance d'un événement de collecte des ordures coûteux pendant le trading." martinfowler.com/articles/lmax.html
JimmyJames
2
Pas assez. Nous avions l'habitude de déclencher un GC manuel chaque nuit dans un intervalle de négociation de 5 minutes, et réglé pour que ce soit le seul GC majeur en un jour. Cela est devenu redondant avec Azul Zing. (Source: J'ai travaillé au LMAX jusqu'à récemment)
Tom Johnson
@TomJohnson J'adore avoir le scoop intérieur. Voulez-vous dire que la description de Martin Fowler est fausse? Est-il possible que la solution ait évolué au fil du temps?
JimmyJames
2
Je dis qu'il n'avait pas exactement raison sur certains détails mineurs. Nous n'avons jamais fait rebondir nos systèmes quotidiennement, mais nous avons effectué un nettoyage de fin de journée.
Tom Johnson
3

Ils devraient penser à bas niveau

Ci-dessus représente la moitié de la réponse que vous recherchez. Vous pouvez trouver une autre moitié pour compléter le raisonnement pas plus loin que dans le blog LMAX :

Bien que très efficace, il peut conduire à un certain nombre d'erreurs car il est très facile de visser ...

Comme admis par les développeurs LMAX, un code comme celui-ci peut être assez difficile à développer, à comprendre et à déboguer - même en Java. Aller plus bas que ce qu'ils sont maintenant ne fera qu'exacerber ce problème, comme le souligne un article de Wikipedia sur les langages de programmation de bas niveau :

Un programme écrit dans un langage de bas niveau peut être exécuté très rapidement et avec une très petite empreinte mémoire; un programme équivalent dans une langue de haut niveau sera plus lourd. Les langages de bas niveau sont simples, mais sont considérés comme difficiles à utiliser, en raison des nombreux détails techniques dont il faut se souvenir .

Par comparaison, un langage de programmation de haut niveau isole la sémantique d'exécution d'une architecture informatique de la spécification du programme, ce qui simplifie le développement ...

moucheron
la source
3

Si vous utilisez Java comme langage de syntaxe et évitez ses bibliothèques JDK, il peut être aussi rapide qu'un langage non GC compilé. GC n'est pas adapté aux systèmes en temps réel, mais il est possible de développer des systèmes en Java qui ne laissent aucun déchet. Par conséquent, le GC ne se déclenche jamais.

Nous pensons que le langage et la plate-forme Java présentent de nombreux avantages par rapport à C / C ++ et nous avons développé et comparé certains composants Java à très faible latence pour le prouver. Nous parlons des techniques pour le faire dans cet article: Développement Java sans GC .

rdalmeida
la source
2
Il existe des récupérateurs adaptés aux systèmes en temps réel. Le collecteur par défaut de la JVM ne l'est peut-être pas, mais cela ne signifie pas que le GC en général n'est pas adapté au temps réel. Mais plain malloc/freene convient pas non plus au temps réel, car le temps d'allocation n'est pas limité en raison de la fragmentation.
Doval
1
Nous préconisons l'utilisation de pools d'objets rapides pour tout afin qu'aucune allocation ne se produise après l'échauffement.
rdalmeida
2

LMAX est une bibliothèque de messagerie inter-threads haute performance.

Pour être utile, quelqu'un d'autre doit écrire le code pour que chaque thread fasse un travail utile. Étant donné que le code est le plus susceptible d'être en Java ou en C #, il y a très peu de choix de langage qui s'interfacent bien avec eux.

L'utilisation de C ou C ++ n'est pas une bonne option, sauf si vous souhaitez limiter vos utilisateurs à un seul système d'exploitation, car aucun modèle de thread n'est défini en eux.

Java est la norme pour beaucoup de développement de logiciels de nos jours, donc à moins que vous n'ayez une bonne raison, il a tendance à être le meilleur choix. (Quand à Rome faites comme les Romains…)

L'écriture de logiciels Haute Performance en Java (ou C #) se fait souvent pour prouver un point…

Ian
la source
1
La nouvelle norme C ++ 11 prend en charge le multithreading ...
Casey
@Casey, et combien de compilateurs C ++ du monde réel l'utilisent? Et combien coûtent ces compilateurs. Peut-être que dans 20 ans, ce sera utile, jusque-là, vous ne pouvez pas en dépendre.
Ian
Disruptor utilise un peu sun.misc.Unsafe, ce qui montre que vous ne pouvez pas vraiment écrire de code à faible latence en Java sans plonger votre orteil dans le pays C
James
3
Gcc prend en charge les threads C ++ et c'est gratuit
James
@Ian: 2 ans plus tard et tous les compilateurs d'usage général le supportent;). Même ceux qui sont gratuits.
Rutix