Une raison de surcharger la nouvelle et la suppression globale?

À moins que vous ne programmiez des parties d’un système d’exploitation ou d’un système intégré, y a-t-il des raisons de le faire? Je peux imaginer que pour certaines classes particulières créées et détruites, la surcharge des fonctions de gestion de la mémoire ou l’introduction d’un pool d’objects peut entraîner une baisse de la charge, mais globalement?

Une addition
Je viens de trouver un bug dans une fonction de suppression surchargée – la mémoire n’était pas toujours libérée. Et c’était dans une application pas si critique en mémoire. En outre, la désactivation de ces surcharges réduit les performances de ~ 0,5% seulement.

Nous surchargons les opérateurs globaux new et delete où je travaille pour de nombreuses raisons:

  • regroupement de toutes les petites allocations – diminue les frais généraux, réduit la fragmentation, peut améliorer les performances des petites applications à forte allocation
  • les allocations de cadrage avec une durée de vie connue – ignorez toutes les libérations jusqu’à la toute fin de cette période, puis libérez-les toutes ensemble (il est vrai que nous le faisons plus avec les surcharges d’opérateurs locaux que globales)
  • ajustement de l’ alignement – aux limites de la cache, etc.
  • allocation d’allocation – aide à exposer l’utilisation de variables non initialisées
  • remplissage gratuit – aide à exposer l’utilisation de la mémoire précédemment supprimée
  • retardé gratuitement – augmentant l’efficacité du remplissage gratuit, augmentant parfois les performances
  • sentinelles ou clôtures – aidant à exposer les dépassements de tampon, les dépassements et le pointeur sauvage occasionnel
  • redirect les allocations – pour tenir compte de NUMA, de zones de mémoire spéciales, ou même pour séparer les systèmes séparés en mémoire (par exemple pour les langages de script intégrés ou les DSL)
  • nettoyage ou nettoyage des ordures – à nouveau utile pour les langages de script intégrés
  • vérification du tas – vous pouvez parcourir la structure de données du tas chaque N allocs / libère pour vous assurer que tout semble correct
  • la comptabilité , y compris le suivi des fuites et les instantanés / statistiques d’utilisation (stacks, âges d’allocation, etc.)

L’idée de la comptabilité nouvelle / suppression est vraiment flexible et puissante: vous pouvez, par exemple, enregistrer l’intégralité de la stack d’appel pour le thread actif chaque fois qu’une atsortingbution se produit, et agréger des statistiques à ce sujet. Vous pouvez envoyer les informations de la stack sur le réseau si vous ne disposez pas de l’espace nécessaire pour le conserver localement pour quelque raison que ce soit. Les types d’informations que vous pouvez rassembler ici ne sont limités que par votre imagination (et votre performance, bien sûr).

Nous utilisons des surcharges globales car il est pratique d’y installer de nombreuses fonctionnalités de débogage courantes, ainsi que d’apporter des améliorations radicales à l’ensemble de l’application, en fonction des statistiques recueillies à partir de ces mêmes surcharges.

Nous utilisons toujours des allocateurs personnalisés pour les types individuels. Dans de nombreux cas, l’accélération ou les capacités que vous pouvez obtenir en fournissant des allocateurs personnalisés, par exemple, un point d’utilisation unique d’une structure de données STL, dépassent de loin la vitesse générale que vous pouvez obtenir des surcharges globales.

Jetez un coup d’œil à certains des allocators et systèmes de débogage qui existent pour C / C ++ et vous trouverez rapidement ces idées et d’autres:

  • valgrind
  • clôture élecsortingque
  • dmalloc
  • dlmalloc
  • Vérificateur d’application
  • Assurer ++
  • BoundsChecker
  • … et beaucoup d’autres … (l’indussortinge gamedev est un endroit idéal pour regarder)

(Un livre ancien mais novateur est: Writing Solid Code , qui traite de nombreuses raisons pour lesquelles vous pourriez vouloir fournir des allocateurs personnalisés en C, dont la plupart sont toujours très pertinents.)

Évidemment, si vous pouvez utiliser l’un de ces outils, vous voudrez le faire plutôt que de le faire vous-même.

Il y a des situations dans lesquelles il est plus rapide, plus facile, moins compliqué pour les affaires et les lois, rien n’est disponible pour votre plate-forme ou simplement plus instructif: creusez et écrivez une surcharge globale.

La raison la plus courante pour surcharger New et Delete est simplement de vérifier les memory leaks et les statistiques d’utilisation de la mémoire. Notez que la “fuite de mémoire” est généralement généralisée aux erreurs de mémoire. Vous pouvez vérifier des éléments tels que les doubles suppressions et les dépassements de tampon.

Les utilisations ultérieures sont généralement des schémas d’allocation de mémoire, tels que le nettoyage de la mémoire et le regroupement .

Tous les autres cas ne sont que des éléments spécifiques, mentionnés dans d’autres réponses (connexion au disque, utilisation du kernel).

Outre les autres utilisations importantes mentionnées ici, telles que l’étiquetage de mémoire, c’est également le seul moyen de forcer l’allocation de blocs fixes à toutes les allocations de votre application, ce qui a des implications énormes sur les performances et la fragmentation.

Par exemple, vous pouvez avoir une série de pools de mémoire avec des tailles de bloc fixes. Le remplacement de global new vous permet de diriger toutes les allocations de 61 octets vers, par exemple, le pool de blocs de 64 octets, toutes les atsortingbutions de 768-1024 octets vers le pool de blocs 1024b, vers le pool de blocs de 2048 octets. plus grand que 8kb au tas en lambeaux général.

Étant donné que les allocators de blocs fixes sont beaucoup plus rapides et moins enclins à la fragmentation que l’allocation de bon à rien, cela vous permet de forcer un code de partie 3D minable à être alloué à partir de vos pools et non à tout l’espace d’adressage.

Cela se fait souvent dans des systèmes critiques en termes de temps et d’espace, tels que les jeux. 280Z28, Meeh et Dan Olson ont expliqué pourquoi.

UnrealEngine3 surcharge globalement new et delete dans le cadre de son système de gestion de la mémoire principale. Plusieurs allocateurs proposent des fonctionnalités différentes (profilage, performance, etc.) et ils ont besoin de toutes les allocations pour passer au travers.

Edit: Pour mon propre code, je ne le ferais qu’en dernier recours. Et par là je veux dire que je ne l’aurais presque jamais utilisé. Mais mes projets personnels sont évidemment des exigences beaucoup plus petites et très différentes.

Certains systèmes temps réel les surchargent pour éviter leur utilisation après init.

La surcharge de new & delete permet d’append une balise à vos allocations de mémoire. Je marque les allocations par système ou contrôle ou par middleware. Je peux voir, à l’exécution, combien chacun utilise. Peut-être que je veux voir l’utilisation d’un parsingur séparé de l’interface utilisateur ou de la quantité d’utilisation d’un middleware!

Vous pouvez également l’utiliser pour placer des bandes de garde autour de la mémoire allouée. Si / quand votre application se bloque, vous pouvez consulter l’adresse. Si vous voyez le contenu comme “0xABCDABCD” (ou celui que vous choisissez comme garde), vous accédez à la mémoire que vous ne possédez pas.

Après avoir appelé delete, vous pouvez peut-être remplir cet espace avec un motif reconnaissable similaire. Je crois que VisualStudio fait quelque chose de similaire dans le débogage. Ne remplit-il pas la mémoire non initialisée avec 0xCDCDCDCD?

Enfin, si vous avez des problèmes de fragmentation, vous pouvez l’utiliser pour redirect vers un allocateur de blocs? Je ne suis pas sûr de la fréquence à laquelle cela pose un problème.

Vous devez les surcharger lorsque l’appel à new et à delete ne fonctionne pas dans votre environnement.

Par exemple, dans la programmation du kernel, les parameters par défaut new et delete ne fonctionnent pas car ils utilisent une bibliothèque en mode utilisateur pour allouer de la mémoire.

D’un sharepoint vue pratique, il peut être préférable de remplacer malloc au niveau de la bibliothèque système, car l’opérateur new l’appellera probablement de toute façon.

Sous Linux, vous pouvez mettre votre propre version de malloc à la place du système, comme dans cet exemple ici:

http://developers.sun.com/solaris/articles/lib_interposers.html

Dans cet article, ils essaient de collecter des statistiques de performances. Mais vous pouvez également détecter les memory leaks si vous écrasez également gratuitement.

Comme vous le faites dans une bibliothèque partagée avec LD_PRELOAD, vous n’avez même pas besoin de recomstackr votre application.

Je l’ai vu dans un système qui, pour des raisons de «sécurité» * , était nécessaire pour écrire toute la mémoire utilisée lors de la désaffectation. L’approche consistait à allouer quelques octets supplémentaires au début de chaque bloc de mémoire, ce qui contiendrait la taille du bloc global qui serait alors écrasé par des zéros lors de la suppression.

Cela pose un certain nombre de problèmes, comme vous pouvez probablement vous l’imaginer, mais cela fonctionne (principalement) et évite à l’équipe de revoir chaque allocation de mémoire dans une application assez grande et existante.

Certainement pas en disant que c’est un bon usage mais c’est probablement l’un des plus imaginatifs là-bas …

* malheureusement, il ne s’agissait pas tant de sécurité réelle que d’apparence de sécurité …

Les plug-ins Photoshop écrits en C ++ devraient remplacer l’ operator new afin qu’ils obtiennent de la mémoire via Photoshop.

Je l’ai fait avec des fichiers mappés en mémoire afin que les données écrites dans la mémoire soient automatiquement enregistrées sur le disque.
Il est également utilisé pour renvoyer de la mémoire à une adresse physique spécifique si vous avez des périphériques d’E / S mappés en mémoire, ou parfois si vous devez allouer un certain bloc de mémoire contiguë.

Mais 99% du temps est fait en tant que fonctionnalité de débogage pour consigner la fréquence, l’endroit, l’allocation et la libération de la mémoire.

En fait, il est courant que les jeux allouent une énorme quantité de mémoire au système, puis fournissent des allocateurs personnalisés via une nouvelle surcharge ou une suppression. L’une des principales raisons est que les consoles ont une taille de mémoire fixe, ce qui pose de gros problèmes de fuites et de fragmentation.

Habituellement (au moins sur une plate-forme fermée), les opérations de tas par défaut entraînent un manque de contrôle et un manque d’introspection. Pour de nombreuses applications, cela n’a pas d’importance, mais pour que les jeux fonctionnent de manière stable dans des situations de mémoire fixe, le contrôle et l’introspection supplémentaires sont tous deux extrêmement importants.

Cela peut être une bonne astuce pour que votre application soit capable de répondre à des conditions de mémoire insuffisante par autre chose qu’un plantage aléatoire. Pour ce faire, votre new peut être un simple proxy pour le new par défaut qui détecte ses échecs, libère des trucs et réessaye.

La technique la plus simple consiste à réserver un bloc de mémoire vierge au démarrage pour cette fin. Vous pouvez également avoir du cache dans lequel vous pouvez puiser – l’idée est la même.

Lorsque le premier échec d’allocation intervient, vous avez encore le temps d’avertir votre utilisateur des conditions de mémoire insuffisante (“Je pourrai survivre un peu plus longtemps, mais vous voudrez peut-être enregistrer votre travail et fermer d’autres applications”), Enregistrez votre état sur le disque, passez en mode de survie ou tout autre élément utile dans votre contexte.

Le cas d’utilisation le plus courant est probablement la vérification des fuites.

Un autre cas d’utilisation est lorsque vous avez des exigences spécifiques pour l’allocation de mémoire dans votre environnement qui ne sont pas satisfaites par la bibliothèque standard que vous utilisez, comme par exemple, vous devez garantir que l’allocation de mémoire est libre dans un environnement multi-threadé.

Comme beaucoup l’ont déjà indiqué, cela se fait généralement dans des applications critiques, ou pour contrôler l’alignement de la mémoire ou suivre votre mémoire. Les jeux utilisent souvent des gestionnaires de mémoire personnalisés, en particulier pour cibler des plates-formes / consoles spécifiques.

Voici un très bon article sur un moyen de le faire et un raisonnement.