11
votes

Comment les collectionneurs à ordures peuvent-ils être plus rapides que la classifocation de la mémoire explicite?

Je lisais ce Généré HTML (peut expirer , Voici le fichier PS original. )

gc Mythe 3: Les collectionneurs à ordures sont toujours plus lents que la classification de la mémoire explicite.
GC Mythe 4: Les collectionneurs à la poubelle sont toujours plus rapides que la classification de la mémoire explicite.

C'était un gros WTF pour moi. Comment GC serait-il plus rapide que la classifocation de mémoire explicite? Cela n'appelle-t-il pas essentiellement un problème de la mémoire explicite lorsqu'il libère la mémoire / le faire à nouveau? Alors .... wtf .... qu'est-ce que cela signifie réellement?

Très petits objets et grands clairsemés tas ==> gc est généralement moins cher, surtout avec des threads

Je ne comprends toujours pas ça. C'est comme dire C ++ est plus rapide que le code de la machine (si vous ne comprenez pas le WTF dans cette phrase, arrêtez la programmation. Laissez le -1 commencer). Après une source rapide Google One, une source a suggéré plus vite lorsque vous avez beaucoup de mémoire. Ce que je pense, c'est que cela signifie que cela ne se dérange pas du tout. Bien sûr, cela peut être rapide et j'ai écrit un allocator personnalisé qui fait cette chose très chose, pas libre du tout ( vide gratuit (vide * p) {} ) dans une application qui ne libère aucun objet (il Seulement libère à la fin lorsqu'elle se termine) et a la définition principalement en cas de libs et de quelque chose comme STL. Alors ... je suis sûr que ce sera aussi plus rapide du GC. Si je veux toujours libérer, je suppose que je peux utiliser un allocator qui utilise une drique ou sa propre implémentation, essentiellement xxx

que je suis sûr serait vraiment rapide. J'ai quelque chose de répondu à ma question déjà. Le cas que le collecteur de GC n'est jamais appelé, c'est le cas, ce serait plus rapide, mais je suis sûr que ce n'est pas ce que le document signifie qu'il mentionne deux collectionneurs dans son test. Je suis sûr que la même application serait plus lente si le collecteur de GC est appelé même une fois que GC a utilisé. Si sa connue ne nécessite jamais libre, un corps libre vide peut être utilisé comme une application que j'avais.

Quoi qu'il en soit, je pose cette question pour une perspicacité supplémentaire.


5 commentaires

Exemple échoue. C ++ peut en fait, pour tout à plus grande échelle, être plus rapide que le code de la machine, car les compilateurs C ++ sont notoirement meilleurs que les programmeurs d'assemblage à l'écriture de code de machine à proximité, lorsque l'humain a abandonné il y a longtemps la santé mentale et le fait très systématiquement même lorsque générer des milliards d'instructions. Et cela suppose que vous utilisiez des performances d'exécution d'un programme donné, n'incluant pas si le programme est terminé du tout.


@delnan: Je n'ai pas voulu dire "écrire". Également dans le cas de SSE et de tels compilateurs ne savent pas que cela peut utiliser ou savoir où utiliser les instructions spéciales.


Vous sous-estimez les compilateurs. Plusieurs compilateurs les utilisent depuis un certain temps . Voir aussi Stackoverflow.com/questions/405770/whay-are -Compilers-So-Stup ID , qui ne correspond pas tout à fait cela, mais inclut toujours quelques exemples de GCC générant un code très optimal.


VC ++ génère un code SSE charmant. Il suffit de définir l'option dans "Propriétés" et de la regarder Go.


"C ++ peut en fait, pour tout à plus grande échelle, être plus rapide que le code de la machine, car les compilateurs C ++ sont notoirement meilleurs que les programmeurs d'assemblage à l'écriture d'un code de machine proche idéal". J'ai étudié cela plusieurs fois au fil des ans et j'ai trouvé du temps et de la fois qu'il n'est pas vrai: les compilateurs produisent un code de machine assez pauvre. J'ai encore étudié l'année dernière et j'ai trouvé une fois de plus que je (sans expertise X86) pourrait facilement écrire un meilleur assembleur que GCC. Flyingfrogblog.blogspot.co.uk/2012/04/ ...


4 Réponses :


15
votes

Une approche pour faire de GC plus rapidement, la répartition explicite est de négocier implicitement:

Le tas est divisé en partitions et les commutateurs VM entre les partitions de temps à autre (lorsqu'une partition devient trop pleine. ). Les objets en direct sont copiés dans la nouvelle partition et tous les objets morts ne sont pas distribués - ils ne sont que oubliés. Donc, la répartition elle-même finit de ne rien coûter. Le bénéfice supplémentaire de cette approche est que la défragmentation du tas est un bonus gratuit.

Veuillez noter que ceci est une description très générale des processus réels.


1 commentaires

Je l'aime bien. Je n'ai jamais pensé à un bonus de vitesse en raison de la défragmentation et de la montage de la mémoire dans une page moins de cache. Joli. +1



11
votes

Le truc est que l'allocator sous-jacent pour le collecteur des ordures peut être beaucoup plus simple que celui explicite et prendre quelques raccourcis que l'on ne peut pas.

  1. Si le collecteur copie (Java et .NET et OCAML et HASKELL RUNTIMES et de nombreux autres utilisent réellement une), la libération est effectuée dans de gros blocs et l'allocation est juste une incrément de pointeur et le coût est payé par une collection survivante. Il est donc plus rapide, surtout quand il y a beaucoup d'objets temporaires de courte durée, ce qui est assez courant dans ces langues.
  2. Même pour un collecteur de non-copie (comme le Boehm's One), le fait que des objets libérés dans des lots économise beaucoup de travail dans la combinaison des morceaux libres adjacents. Donc, si la collection n'a pas besoin de fonctionner trop souvent, elle peut facilement être plus rapide.
  3. et bien, de nombreuses implémentations Standard Bibliothèque Malloc / Free Suck. C'est pourquoi il y a des projets comme umem et bibliothèques comme glib a sa propre version de poids léger.

2 commentaires

+1. Une question. Comment fonctionne le travail du pointeur? Je pense que quelque chose comme PTR + = chunksize ne fonctionnera pas depuis qu'il y aura de nombreux trous en mémoire et vous saurez qui est disponible. Donc, peut-être avoir une liste de quelle mémoire a déplacé et change le PTR chaque fois qu'il le trouve. Ce dernier semble que cela puisse fonctionner, mais c'est une supposition. Savez-vous quelle bonne implémentation peut faire?


@acidzombie: Copier GCS Réservez un gros morceau et utilisez cela. Le pointeur se déplace seulement . Il n'est pas décrémenté lorsqu'il est compréhensible, spécifiquement pour éviter de traiter des trous (sauf peut-être comme une optimisation lorsque le "haut" est distribué, c'est-à-dire qu'aucun objet en direct reste entre le pointeur et l'objet de distribution). Lorsque la zone actuelle est épuisée, tous les objets en direct sont copiés dans une nouvelle zone (sans trous) et le pointeur commence après le dernier objet. (Voir aussi blogs.msdn.com/b/abhinaba/archive/2009/02/02/... )



28
votes

Comment gc serait-il plus rapide que la répartition explicite de la mémoire?

  1. GCS peut le pointeur-bump Allocate dans une génération de fil-thread-local, puis comptez sur la collecte de copier pour gérer le cas (relativement) inhabituel d'évacuer les survivants. Les allocateurs traditionnels tels que MALLOC sont souvent en concurrence pour les serrures globales et les arbres de recherche.

  2. GCS peut annuler de nombreux blocs morts simultanément en réinitialisant le tampon d'allocation de thread-local au lieu d'appeler gratuit sur chaque bloc à tour de rôle, c'est-à-dire O (1) au lieu de O (n).

  3. En compactant d'anciens blocs, donc plus d'entre eux entrent dans chaque ligne de cache. La localité améliorée augmente l'efficacité du cache.

  4. en tirant parti des informations statiques supplémentaires telles que des types immuables.

  5. en tirant parti des informations dynamiques supplémentaires telles que la topologie changeante du tas via les données enregistrées par la barrière d'écriture.

  6. En faisant des techniques plus efficaces, par exemple En supprimant le mal de tête de la gestion de la mémoire manuelle des algorithmes sans attente.

  7. En diffusant la répartition de la distribution plus appropriée ou en le chargement d'un autre noyau. (Merci à Andrew Hill pour cette idée!)


3 commentaires

7. En diffusant la désaffectation à une période optimale de la conflit de la CPU autrement minime (à un coût de l'utilisation mineure de la RAM supplémentaire), et de le faire dans un fil principal de la principale.


Cela se lit presque comme: une GC de la technologie «très soigneusement conçue et optimisée» battue un allocator traditionnel «naïf». Je dirais que ce n'est pas une comparaison équitable. Lequel de ces 7 est vraiment impossible pour un allocator plus avancé? Par exemple, un allocateur peut également garder une trace des magasins de fil-locaux, de rechercher des arbres et d'éviter les serrures globales.


Tous les 1 à 6 sont impossibles à faire d'un allocateur seul.



1
votes

Un facteur non encore mentionné est que lorsque vous utilisez une allocation de mémoire manuelle, même si les références d'objet sont garanties pour ne pas former de cycles, déterminer lorsque la dernière entité à contenir une référence est abandonnée, il peut généralement être coûteux, nécessitant généralement l'utilisation de comptoirs de référence. , Listes de référence ou d'autres moyens de suivi de l'utilisation d'objets. Ces techniques ne sont pas trop mauvaises sur les systèmes à un seul processeur, où le coût d'un incrément atomique peut être essentiellement identique à celui ordinaire, mais ils échouent très mal sur des systèmes multi-processeurs, où les opérations d'incrément atomique sont relativement coûteuses.


0 commentaires