J'ai une petite classe (16 octets sur un système de 32 bits) que j'ai besoin d'allouer de manière dynamique. Dans la plupart des cas, la durée de vie d'une instance donnée est très courte. Certains instances peuvent également être transmis à travers les limites de fil. P>
Ayant fait du profilage, j'ai constaté que mon programme semble dépenser plus de temps à allouer et à transposer les choses que ce qu'il ne les dépensent réellement que je veux remplacer le nouveau et supprimer par défaut avec quelque chose d'un peu plus efficace. p>
Pour un gros objet (Connections à base de DB, qui est coûteux à construire plutôt que d'allouer), j'utilise déjà un système de mise en commun, cependant, qui implique une liste pour stocker les objets «gratuits», ainsi que un mutex. pour la sécurité du fil. Entre le mutex et la liste, il effectue réellement pire qu'avec le nouveau / Supprimer de base pour les petits objets. P>
J'ai trouvé un certain nombre de petits allocateurs d'objets sur Google, mais ils semblent utiliser une piscine globale / statique qui n'est pas utilisée de manière sans fil, ce qui les rend inappropriés à mon utilisation: ( p>
Quelles autres options ai-je eu pour une gestion efficace de la mémoire de si petits objets? p>
3 Réponses :
Vous pouvez vous assurer que vous utilisez le Heap à faible fragmentation A >. Il est par défaut à Vista et plus tard, mais je ne pense pas que cela soit ainsi avec le système d'exploitation précédent. Qui peut faire une grande différence de vitesse d'attribution pour les petits objets. P>
Eh bien, je teste sur Vista, donc dans ce cas, c'est déjà sur. Ça vaut la peine de savoir depuis que je suis soutenant XP.
C'est un problème intéressant (plus que je n'ai pas à le résoudre :). Je suis curieux de voir la solution finale.
Peut-être essayez peut-être d'utiliser Google's TCMALLOC ? Il est optimisé pour une allocation rapide / une distribution de distribution dans un programme fileté et a des frais généraux faibles pour les petits objets. P>
Certains instances peuvent également être transmis à travers les limites de fil i> p>
Seulement "Certains"? Donc, peut-être que vous pouvez vous permettre de payer un supplément pour ceux-ci, si cela rend ceux qui ne sont pas transmis à d'autres threads moins chers. Il existe différentes façons de penser à accéder à un allocator par fil et à éviter la nécessité de verrouiller lors de l'allocation ou de la liberté du fil auquel appartient l'allocator. Je ne sais pas qui pourrait être possible dans votre programme: P>
copier des choses sur la limite de fil, au lieu de les transmettre. p> li>
arrange que s'ils sont passés à un autre fil pour une raison quelconque, ils sont retournés au fil d'origine à la liberté. Cela ne doit pas nécessairement se produire très souvent, vous pouvez faire la queue de quelques-uns dans le fil de réception et les transmettre à tous dans un message plus tard. Cela suppose bien sûr que le fil qui possède l'allocator va coller. P> li>
Dez deux listes libres par allocator, une synchronisée (à laquelle des objets sont ajoutés lorsqu'ils sont libérés d'un autre fil) et d'une non-synchronisée. Ce n'est que si la liste non synchronisée est vide et que vous allociez (et donc dans le fil qui possède l'allocator), devez-vous verrouiller la liste libre synchronisée et déplacer tout son contenu actuel sur la liste non synchronisée. Si des objets transmis à d'autres threads sont rares, cela élimine essentiellement la conflit sur le mutex et réduit massivement le nombre de fois qu'il est pris du tout. P> li>
Si tout ce qui précède échoue, la présentation d'un allocator par thread pourrait toujours vous permettre de vous débarrasser du mutex et d'utiliser une file d'attente sans verrouillage pour la liste gratuite (multiples écrivains libérant, allouant un seul lecteur), ce qui pourrait améliorer un peu les performances. La mise en œuvre d'une file d'attente sans verrouillage est spécifique à la plate-forme. P> LI> ul>
Prendre un pas plus loin de retour, votre application a-t-elle fréquemment touché un état dans lequel vous savez que toutes les cellules allouées après un certain point (peut-être un peu dans le passé), ne sont plus utilisées? Si tel est le cas, et en supposant que le destructeur de vos petits objets ne fait rien de terriblement urgent, alors ne vous dérangeez pas de libérer des cellules du tout - au "certain point", créez un nouvel allocator et marquez l'ancien comme étant plus utilisé pour Nouvelles allocations. Lorsque vous «frappez l'état», libérez tout l'allocateur et sa mémoire tampon sous-jacente. Si le "point de certains points" et "l'état" sont simultanés, tous les plus faciles - il suffit de réinitialiser votre allocator. P>
"Après un certain point (peut-être un peu dans le passé), ne sont plus utilisés?" Malheureusement, non, ils sont utilisés tout au long de la vie du programme. Suivant sur l'un-allocator par fil. Et si je faisais un allocator de piscine qui avait une liste gratuite dans une sorte de TLS. Ensuite, il suffit de nécessiter un processus synchronisé occasionnel pour lutter contre la possibilité d'objets migrant d'un seul thread à un autre, ce qui lui permettra de libérer plus qu'il n'allocie?
Oui, cela semble plausible. Plus la dérive est lente d'un fil à un autre, le moins souvent que vous devez synchroniser et plus bas la surcharge.
Vous pouvez également jeter un coup d'œil à ceci: goog-perfools.sourceforge.net/doc/tcmalloc .html
Si votre objet est très petit, pourquoi pas seulement passer par la valeur? De plus, vos nombreux petits objets sont-ils identiques, ou sont-ils tous différents? Si le premier, regarde Boost.flyweight
Il y a peu de preuves dans votre question qui suggère que vous pouvez réellement faire mieux. Qu'est-ce qui te fait penser que tu fais?
Lorsque vous écrivez mutex, voulez-vous dire en général ou mutex en particulier? Si en particulier, la critique sera mieux tant que vous ne traverez pas les limites de processus.
Celui que je utilise est boost :: mutex que IIRC est implémenté via l'API imbriquée sous Windows. Je ne pense pas qu'il y ait une différence majeure de performance par rapport à la critique.
Deux cas sont très peu susceptibles d'être les mêmes. Il est alloué de manière dynamique car il est courant que plusieurs objets les référencent, et ils doivent tous voir des changements. Étant donné que les solutions de piscine à threads unique ont conduit à une augmentation de vitesse majeure, j'espère qu'il y a une solution de fil-sécurité presque aussi rapide. Même un allocator de tas optimisé pour des objets de taille fixe pourrait bien être plus rapide qu'un identifiant d'allocator à usage général. Attendez-vous et pouvait totalement éviter le problème de fragmentation, mais je n'ai aucune idée de savoir comment ID écrivez aucun allocator de tas pour commencer par :(