9
votes

Défragmentant Allocator C ++ Heap & STL

Je cherche à écrire un gestionnaire de mémoire défragmentant à l'auto-défragmentation, un simple allocator d'un tas d'incrémentation est utilisé en combinaison avec un défragmentant de compactage simple.

Le schéma approximatif consisterait à allouer des blocs à partir de l'adresse de mémoire la plus basse à la hausse et à la conservation des informations de comptage de livres à partir de l'adresse de mémoire la plus élevée du travail.

Le gestionnaire de mémoire dépasserait les pointeurs intelligents - Boost's intrusif_ptr's semble le plus évident pour les structures de conservation des livres qui diraient-elles elles-mêmes au bloc de mémoire réelle qui donnait ainsi un niveau d'indirection afin que les blocs puissent être facilement déplacés.

Le défragmentant compacterait le tas de démarrage des signets de "génération" pour accélérer le processus et ne défragmenter qu'une quantité fixe de mémoire à la fois. Les pointeurs bruts des blocs eux-mêmes seraient valables jusqu'à ce que le prochain passage de défragmentation et afin que cela puisse être transmis librement jusqu'à ce que de telles performances améliorent.

L'application spécifique de ceci est la programmation du jeu de la console et donc au début ou à la fin de chaque image, une passe de défragmentation pourrait être effectuée relativement en toute sécurité.

Donc, ma question est que quelqu'un a utilisé ce type de schéma d'allocation en combinaison avec STL serait-il complètement à l'écart que je soupçonne. Je peux voir STD :: Liste Travailler au niveau intrusif_ptr, mais qu'en est-il de l'attribution des nœuds de la liste STL eux-mêmes est là de toute façon pour remplacer les pointeurs suivants / prev pour être intrusion_ptr's eux-mêmes ou je vais juste devoir avoir une Allocator de tas standard le long de la face plus dynamique.


2 commentaires

Je ne comprends pas. Vous avez une petite poignée Smart_Ptr qui vous permet de déplacer le bloc sous-jacent. Mais que se passe-t-il si le bloc sous-jacent a des pointeurs? Comment forcez-vous ces PTR d'être smart_ptrs?


@JMUCCHIELLO: L'allocator renvoie purement des blocs de mémoire à l'utilisateur à l'utilisateur pour vous assurer que si votre création d'une classe avec ce morceau de mémoire utilise SMART_PTRS pour tous les pointeurs qu'il peut contenir. C'est parfaitement bon dans mon cas car j'ai le contrôle des classes et utilisez les intrusifs_ptrs pour «posséder» d'autres objets de toute façon.


5 Réponses :


0
votes

Les conteneurs STL sont mis en œuvre avec des pointeurs nus.

Vous pouvez spécifier un allocator personnalisé lorsque vous les instanciez (ils initialisent donc leurs pointeurs à l'aide de votre allocator), mais (parce que les valeurs allouées sont stockées dans des pointeurs nus), vous ne savez pas où ces pointeurs sont, et donc vous ne peut pas les changer plus tard.

Au lieu de cela, vous pouvez envisager de mettre en œuvre un sous-ensemble de la STL vous-même: vos versions des conteneurs STL pourraient ensuite être implémentées avec des pointeurs gérés.


8 commentaires

Pour cela, je vous recommande de jeter un coup d'œil à boost.interprocess, où un offset_ptr est utilisé pour les régions de mémoire partagées.


J'avais soupçonné une mise en œuvre personnalisée STL serait nécessaire qui est une vraie honte. Je suppose qu'une implémentation stl correctement faite, telle que Eastl, ne serait pas si mauvaise, mais prendrait une juste quantité de temps pour écrire et tester. Est-ce que vous ou quelqu'un d'autre connaissez une personne ayant écrit une telle mise en œuvre STL? Je semble me souvenir de Palm ayant un schéma d'allocation de mémoire comme celui-ci, je me demande s'ils ont fait quelque chose de raconté ...


J'utilise habituellement seulement un petit sous-ensemble de la STL, c'est-à-dire (en dehors des E / S) que j'utilise std :: String et un ou deux types de conteneurs (et seul un sous-ensemble de la fonctionnalité de ces types); Donc, une mise en œuvre de stl personnalisée ne serait pas si difficile à mes besoins.


Non, je ne connais pas d'une est disponible au public.


"Il semble que je me souvienne de se souvenir de Palm ayant un schéma d'allocation de mémoire comme celui-ci, je me demande s'ils ont fait quelque chose de raconté" - maintenant je me demande (hors de curiosité inactive) si cela pouvait être traité dans du matériel: 1) Votre allocateur retourne des pointeurs à la mémoire non valide 2) code naïf (y compris toute implémentation STL standard) tente d'utiliser ces valeurs de pointeur 3) Il existe un piège à l'autre sur l'accès à un pointeur non valide et un gestionnaire de sonnerie 0 TRAP de la tienne puis fait une sorte de fixupt pour laisser l'application accéder à la mémoire non invalide correspondante.


Cela semble intéressant! Bonne pensée, je vais devoir examiner les implications de la performance de cette question relative aux latences de stockage de données / interruptions de segment et à quel point cela serait debuggable si les choses se passaient mal. Il semblerait que le matériel cible Suspendrait les deux threads de matériel lorsqu'une exception se soit produite afin que cela puisse être plus (d'une perspective de verrouillage) et négatif (d'une perspective de performance). Je ne suis pas si sûr de ce que les problèmes de sécurité impliqués dans ce domaine seraient également à l'égard de l'hyperviseur. Quelque chose de similaire est en cours d'exécution avec la technologie 5 du logiciel ID, je crois.


J'ai de graves doutes que Eastl veut une telle gestion de la mémoire. :) Si vous voulez pirater votre propre stl, je peux suggérer de commencer USTL, c'est simple, compact et lisible. Est-ce que ça marche? Eh bien, apparemment, j'ai entendu dire que cela a été utilisé sur le projet: SnowBlind de la dynamique en cristal sur PS2. Rasé à plus d'un MEG sur les instanciations de modèle. Cependant, comme je le mentionne ailleurs, je ne pense pas que ce soit la bonne direction.


J'ai aussi des doutes graves sur la paume ayant de tels trucs de violence C ++ haut de gamme. Je ne connais pas la paume, je ne connais que Epoc-> Symbian, et je sais qu'ils ne l'ont pas fait, bien qu'ils soient bons à la maxing des systèmes sans s'écraser.



5
votes

Si vous allez déplacer des objets en mémoire, vous ne pouvez pas le faire de manière totalement générale. Vous ne serez capable que de le faire avec des objets qui savent qu'ils pourraient être déplacés. Vous aurez également besoin d'un mécanisme de verrouillage. Lorsqu'une fonction est appelée sur un objet, il ne peut pas être déplacé.

La raison en est que l'ensemble du modèle C ++ s'appuie sur des objets assis à des points fixes en mémoire, de sorte que si un thread appelait une méthode sur un objet, ce fil a été mis en pause et l'objet déplacé, une catastrophe frapperait lorsque le fil a repris.

n'importe quel objet qui détenait un pointeur de mémoire brut sur un autre objet pouvant être déplacé (y compris un sous-objet de lui-même) ne fonctionnerait pas.

Un tel système de gestion de la mémoire peut fonctionner, mais vous devez faire très attention. Vous devez être strict sur les poignées de mise en œuvre et la sémantique de verrouillage du pointeur.

Pour les conteneurs STL, vous pouvez personnaliser l'allocator, mais il doit toujours renvoyer des pointeurs de mémoire bruts fixes. Vous ne pouvez pas retourner une adresse qui pourrait bouger. Pour cette raison, si vous utilisez des conteneurs STL, ils doivent être des conteneurs de poignées et les nœuds elles-mêmes seront une mémoire ordinaire allouée de manière dynamique. Vous constaterez peut-être que vous avez trop de surcharge dans l'indirection de la poignée et que vous avez toujours des problèmes dans la fragmentation des collections de la poignée que vous gagnez à l'aide de STL.

Utilisation de conteneurs comprenant que vos poignées peuvent être la seule voie à suivre, et même à ce moment-là, il peut encore y avoir beaucoup de frais généraux par rapport à une application C ++ utilisant des objets traditionnels fixés en mémoire.


3 commentaires

"Vous ne pourrez faire que cela avec des objets qui savent qu'ils pourraient être déplacés." - et / ou avec des objets que contiennent des objets qui savent qu'ils pourraient être déplacés: c'est-à-dire que vous pourriez avoir une classe "pointeur gérée" et que vous alliez bien avec des classes qui utilisent / contiennent des instances "gérées pointeur "classe au lieu d'utiliser des pointeurs nus.


Il n'a pas besoin de verrouiller car il ne fera que défrailler "entre les cadres", c'est-à-dire que les objets contenant des indicateurs gérés ne sont pas couragés du tout.


Oui Heureusement, nous avons un contrôle assez strict sur la manière dont l'application se comporte et comment les programmeurs utilisent le jeu SDK et nous pouvons donc faire appel à certaines restrictions sur la raison, c'est-à-dire que vous pouvez transmettre des pointeurs bruts dans une mise à jour de l'application, mais qu'ils seront invalide dans la trame suivante; Si vous voulez un pointeur sur tout ce qui doit être conservé sur de nombreux cadres, vous devez utiliser notre type personnalisé intrusion_ptr. Nous faisons ce type de chose de toute façon avec standard intrusion_ptr's.



0
votes

Une technique alternative qui est assez bien connue est le Système de pote . Vous devriez examiner cela pour une inspiration supplémentaire.


0 commentaires

0
votes

S'il s'agit de la programmation du jeu de console, il est beaucoup plus facile d'interdire les allocations de mémoire dynamique non scopées au moment de l'exécution. Et au moment du démarrage, mais c'est un peu difficile à atteindre.


0 commentaires

0
votes

Ma prise en compte, c'est que si vous devez avoir peur de la fragmentation, cela signifie que vous jonglez avec des pièces de données qui sont une énorme fraction de votre mémoire, et par cette seule vertu, vous ne pouvez en avoir beaucoup. Sachez-vous déjà ce que ce sera? Peut-être serait-il préférable de rejeter un niveau et de prendre des décisions plus spécifiques, entravant ainsi moins sur l'autre code et la performance générale de votre application?

Une liste est un exemple exceptionnellement mauvais pour mettre dans un gestionnaire de mémoire défragmentant, car il s'agit d'une bouquet de minuscules pièces, de même que la plupart des autres structures de données STL. Si vous faites cela, il aura toutes sortes de mauvaises implications évidentes - y compris la performance de votre défragmentation en descendant, également le coût indirect, etc. Les seules structures où il est logique que l'IMO est un éventuel, une matrice, une palette, une partie principale de la haquetable , ces choses, et seulement au-delà d'une certaine taille, et seulement après qu'ils ne seront plus redimensionnés. Ce genre de choses appelle, encore une fois, pour des solutions spécifiques, au lieu de génériques.

Commentaire sur la façon dont tout se passe.


0 commentaires