43 votes

Passer le pointeur nul au placement nouveau

L'emplacement par défaut new opérateur est déclaré dans 18,6 [support.dynamique] ¶1 avec un non-lancer spécification d'exception:

void* operator new (std::size_t size, void* ptr) noexcept;

Cette fonction ne fait rien, sauf return ptr; il est donc raisonnable pour qu'il soit noexcept, cependant, selon 5.3.4 [expr.nouveau] ¶15 cela signifie que le compilateur doit vérifier qu'il ne retourne pas null avant d'appeler le constructeur de l'objet:

-15-
[Note: à moins d'une allocation de fonction est déclarée avec un non-lancer spécification d'exception (15.4), il indique un échec d'allocation de stockage en jetant un std::bad_alloc d'exception (article 15, 18.6.2.1); elle renvoie un pointeur non null sinon. Si la fonction d'allocation est déclarée avec un non-lancer spécification d'exception, elle renvoie la valeur null pour indiquer l'échec d'allocation de stockage et un pointeur non null sinon. -fin de la remarque] Si l'attribution de la fonction renvoie la valeur null, l'initialisation ne doit pas être fait, la fonction de libération ne doit pas être appelé, et la nouvelle valeur de l'expression est nulle.

Il me semble que (spécifiquement pour le placement new, pas en général) cette null check est un malheureux performance de hit, mais modeste.

J'ai été le débogage de code où le placement new a été utilisé dans une performance très sensible chemin de code pour améliorer le compilateur de la génération de code et de les vérifier pour les nuls a été observée dans l'assemblée. En fournissant une classe spécifique de placement new de surcharge qui est déclarée avec un lancement spécification d'exception (même si elle ne peut pas les jeter) la branche conditionnelle a été supprimé, ce qui a également permis au compilateur de générer plus petit code pour les environs inline fonctions. Le résultat de dire le placement new fonction pourrait lancer, même si elle ne pouvait pas, a été sensiblement meilleur code.

Donc je me demandais si la valeur null est à vérifier est vraiment nécessaire pour le placement new des cas. La seule façon pour elle de retourner la valeur null est si vous passer la valeur null. Bien qu'il soit possible, et apparemment juridique, d'écrire:

void* ptr = nullptr;
Obj* obj = new (ptr) Obj();
assert( obj == nullptr );

Je ne vois pas pourquoi ce serait utile, je suggère qu'il serait mieux si le programmeur avait pour vérifier la valeur null explicitement avant d'utiliser le placement new par exemple

Obj* obj = ptr ? new (ptr) Obj() : nullptr;

Quelqu'un a besoin de placement new afin de gérer correctement le pointeur null cas? (c'est à dire sans ajout d'une vérification explicite qu' ptr est valide emplacement de la mémoire.)

Je me demandais s'il serait raisonnable d'interdire le passage d'un pointeur null à l'emplacement par défaut new de la fonction, et si pas de savoir si il ya une meilleure façon d'éviter l'inutile branche, d'autres que d'essayer de dire au compilateur que cette valeur n'est pas null par exemple

void* ptr = getAddress();
(void) *(Obj*)ptr;   // inform the optimiser that dereferencing pointer is valid
Obj* obj = new (ptr) Obj();

Ou:

void* ptr = getAddress();
if (!ptr)
  __builtin_unreachable();  // same, but not portable
Obj* obj = new (ptr) Obj();

N. B. Cette question est volontairement tagged micro-optimisation, je suis pas ce qui suggère que vous allez autour de la surcharge de placement new pour tous vos types "d'améliorer" les performances. Cet effet a été remarqué dans un très spécifique de la performance-cas critique et basé sur le profilage et la mesure.

13voto

Arne Mertz Points 13966

Alors que je ne vois pas beaucoup d'une question, à part "quelqu'un A requis le placement de nouveaux afin de gérer correctement le pointeur null cas?" (Je n'ai pas), je pense que le cas est suffisamment intéressant pour le déversement des réflexions sur la question.

Je considère que la norme cassés ou incomplets concernant le placement de nouvelles fonctions et exigences relatives à la répartition des fonctions en général.

Si vous regardez de près à la cité §5.3.4,13, cela implique que chaque fonction d'allocation doit être vérifiée pour un retour nullpointer, même si elle n'est pas noexcept. Par conséquent, il doit être réécrit pour

Si la fonction d'allocation est déclarée avec une qui ne lance pas d'exception à la spécification et renvoie la valeur null, l'initialisation ne doit pas être fait, la fonction de libération ne doit pas être appelé, et la nouvelle valeur de l'expression est nulle.

Qui ne porterait pas atteinte à la validité des fonctions de répartition de lever des exceptions, car ils ont à obéir, §3.7.4.1:

[...] Si elle est réussie, elle doit renvoyer l'adresse de début d'un bloc de stockage dont la longueur en octets doivent être au moins aussi grand que la taille demandée. [...] Le pointeur retourné doit être correctement alignés de sorte qu'il peut être converti en un pointeur de chaque type d'objet avec un fondamental de l'alignement exigence (3.11) et ensuite utilisé pour accéder à l'objet ou le tableau dans le stockage alloué (jusqu'à ce que le stockage est explicitement libéré par un appel à une fonction de libération).

Et §5.3.4,14:

[ Remarque: lors de l'attribution de la fonction renvoie une valeur autre que null, il doit être un pointeur vers un bloc de stockage dans laquelle l'espace de l'objet a été réservé. Le bloc de stockage est supposé être correctement alignés et de la taille demandée. [ ... ] la fin de la note ]

De toute évidence, un placement d'une nouvelle qui vient retourne le pointeur, on ne peut raisonnablement vérifier avilable la taille de stockage et d'alignement. Par conséquent,

§18.6.1.3,1 sur le placement de nouvelles dit

[...] Les dispositions de l' (3.7.4) ne s'appliquent pas à ces place des formes de nouvel opérateur et l'opérateur delete.

(Je suppose qu'ils ont manqué de mentionner §5.3.4,14 à cet endroit.)

Cependant, l'ensemble de ces paragraphes dire indirectement "si vous passez une poubelle pointeur vers le palcement fonctions, vous obtenez UB, parce §5.3.4,14 est violé". Donc, c'est à vous de vérifier la santé mentale de toute poitner donné pour le placement de nouvelles.

Dans cet esprit, et avec le nouveau §5.3.4,13, le standard pourrait priver l' noexcept de placement de nouvelles, conduisant à une plus indirecte conclusion: "...et si vous passez null, vous obtenez de l'UB ainsi". D'autre part, est beaucoup moins susceptibles d'avoir un mauvais alignement de pointeur de pointeur ou de trop peu de mémoire que d'avoir un pointeur null.

Toutefois, cela permettrait d'éliminer la nécessité de vérifier par rapport à null, et ça colle bien à la philosophie de "ne pas payer pour ce que vous n'avez pas besoin". La fonction d'allocation elle-même n'aurait pas besoin de vérifier, parce §18.6.1.3,1 explicitement dit.

Pour arrondir les choses, on pourrait envisager d'ajouter un deuxième surcharge

 void* operator new(std::size_t size, void* ptr, const std::nothrow_t&) noexcept;

Malheureusement, cette proposition de la commission est peu probable d'aboutir à un changement, car il serait de casser le code existant en s'appuyant sur la mise en place de nouveaux être ok avec des pointeurs null.

1voto

Carl Cook Points 76

Une question très intéressante (et réponse), merci pour le partage.

De mon point de vue (pas fondée sur quelque chose en particulier), je m'attends à de placement de nouveau pour retourner un pointeur null en cas d'échec, plutôt que d'appeler un constructeur sur un objet null. De cette façon, c'est le même comportement que la fonction malloc.

Ce que vous proposez est une excellente idée - j'ai testé avec gcc 4.4.6, et je n'ai en effet voir le pointeur null vérifier disparaître si je remplace le placement de nouveau avec une version qui en jette.

Une question - n'a pas l' §18.6.1.3,1 dire que le placement de nouvelles ne peuvent pas être déplacées (en dépit de la gcc et vc++ d'être heureux pour permettre le déplacement)?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X