108 votes

Malloc vs new -- rembourrage différent

Je suis en train de réviser le code C++ de quelqu'un d'autre pour notre projet qui utilise MPI pour le calcul haute performance (10^5 - 10^6 cœurs). Le code est destiné à permettre les communications entre (potentiellement) différentes machines sur différentes architectures. Il a écrit un commentaire qui dit quelque chose du genre :

Nous utiliserions normalement new y delete mais ici j'utilise malloc y free . Ceci est nécessaire parce que certains compilateurs remplissent les données différemment lorsque new est utilisé, ce qui entraîne des erreurs lors du transfert de données entre différentes plateformes. Cela ne se produit pas avec malloc .

Cela ne correspond à rien de ce que je connais des standards. new vs malloc questions.

Quelle est la différence entre new/delete et malloc/free ? suggère l'idée que le compilateur pourrait calculer la taille d'un objet différemment (mais alors pourquoi cela diffère-t-il de l'utilisation de l'option sizeof ?).

malloc & placement nouveau vs. nouveau est une question assez populaire mais qui ne parle que de new en utilisant des constructeurs où malloc ne le fait pas, ce qui n'est pas pertinent dans ce cas.

comment malloc comprend-il l'alignement ? dit que la mémoire est garantie d'être correctement alignée avec soit new o malloc ce qui est ce que j'avais pensé auparavant.

Je pense qu'il a mal diagnostiqué son propre problème dans le passé et qu'il en a déduit que new y malloc donnent des quantités différentes de rembourrage, ce qui, à mon avis, n'est probablement pas vrai. Mais je n'arrive pas à trouver la réponse avec Google ou dans une question précédente.

Aide-moi, StackOverflow, tu es mon seul espoir !

25voto

Steve Jessop Points 166970

IIRC il y a un point délicat. malloc est garanti de retourner une adresse alignée pour tout type standard. ::operator new(n) est seulement garanti pour retourner une adresse alignée pour tout type standard pas plus grand que n et si T n'est pas un type de caractère, alors new T[n] est seulement nécessaire pour retourner une adresse alignée pour T .

Mais cela n'est pertinent que lorsque vous jouez des astuces spécifiques à l'implémentation, comme l'utilisation des derniers bits d'un pointeur pour stocker des drapeaux, ou en vous appuyant sur l'adresse pour avoir plus d'alignement que ce dont elle a strictement besoin.

Cela n'affecte pas le remplissage à l'intérieur de l'objet, qui a nécessairement exactement la même disposition, quelle que soit la façon dont vous avez alloué la mémoire qu'il occupe. Il est donc difficile de voir comment la différence pourrait entraîner des erreurs dans le transfert des données.

Y a-t-il un signe de ce que l'auteur de ce commentaire pense des objets sur la pile ou dans les globaux, si selon lui ils sont "rembourrés comme malloc" ou "rembourrés comme new" ? Cela pourrait donner des indices sur l'origine de l'idée.

Peut-être qu'il est confus, mais peut-être que le code dont il parle est plus qu'une simple différence entre malloc(sizeof(Foo) * n) vs new Foo[n] . Peut-être que c'est plus comme :

malloc((sizeof(int) + sizeof(char)) * n);

vs.

struct Foo { int a; char b; }
new Foo[n];

C'est-à-dire, peut-être qu'il est en disant "J'utilise malloc", mais signifie "J'emballe manuellement les données dans des emplacements non alignés au lieu d'utiliser un struct". En fait, malloc n'est pas nécessaire pour emballer manuellement la structure, mais ne pas s'en rendre compte est un moindre degré de confusion. Il est nécessaire de définir la disposition des données envoyées sur le fil. Différentes implémentations garniront les données différemment lorsque la fonction struct est utilisé.

5voto

justin Points 72871

Votre collègue a peut-être eu new[]/delete[] (c'est l'information que l'implémentation utilise lors de la suppression d'un tableau). Cependant, cela n'aurait pas été un problème si l'allocation commençant à l'adresse renvoyée par la commande new[] ont été utilisés (par opposition à ceux de l'allocateur).

Emballage semble plus probable. Des variations dans les ABIs pourraient (par exemple) résulter en un nombre différent d'octets de queue ajoutés à la fin d'une structure (ceci est influencé par l'alignement, pensez aussi aux tableaux). Avec malloc, la position d'une structure pourrait être spécifiée et donc plus facilement portable à une ABI étrangère. Ces variations sont normalement évitées en spécifiant l'alignement et l'empaquetage des structures de transfert.

3voto

john Points 6193

Je pense que vous avez raison. Le remplissage est effectué par le compilateur et non par new o malloc . Les considérations relatives au remplissage s'appliquent même si vous déclarez un tableau ou une structure sans utiliser la fonction new o malloc du tout. En tout cas, même si je peux voir comment différentes implémentations de new y malloc pourraient causer des problèmes lors du portage de code entre plateformes, je ne vois absolument pas comment ils pourraient causer des problèmes lors du transfert de données entre plateformes.

3voto

Barmar Points 135986

L'agencement d'un objet ne peut pas dépendre du fait qu'il ait été alloué ou non en utilisant malloc o new . Elles renvoient toutes deux le même type de pointeur, et lorsque vous transmettez ce pointeur à d'autres fonctions, elles ne sauront pas comment l'objet a été alloué. sizeof *ptr dépend juste de la déclaration de ptr et non pas comment il a été attribué.

0voto

GradGuy Points 1438

C'est mon hypothèse sur l'origine de cette chose. Comme vous l'avez dit, le problème vient de la transmission des données sur MPI.

Personnellement, pour mes structures de données compliquées que je veux envoyer/recevoir via MPI, j'implémente toujours des méthodes de sérialisation/désérialisation qui empaquettent/dépaquettent le tout dans/à partir d'un tableau de caractères. Maintenant, à cause du remplissage, nous savons que la taille de la structure peut être plus grande que la taille de ses membres et donc il faut aussi calculer la taille non remplie de la structure de données afin de savoir combien d'octets sont envoyés/reçus.

Par exemple, si vous voulez envoyer/recevoir std::vector<Foo> A sur MPI avec ladite technique, il est faux de supposer que la taille du tableau de caractères résultant est A.size()*sizeof(Foo) en général. En d'autres termes, chaque classe qui implémente des méthodes de sérialisation/désérialisation, devrait également implémenter une méthode qui rapporte la taille du tableau (ou mieux encore, stocker le tableau dans un conteneur). Ce site pourrait deviennent la raison d'être d'un bug. D'une manière ou d'une autre, cependant, cela n'a rien à voir avec new vs malloc comme cela a été souligné dans ce fil.

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