Malloc vs new - andere Polsterung


110

Ich überprüfe den C ++ - Code eines anderen für unser Projekt, das MPI für Hochleistungsrechnen verwendet (10 ^ 5 - 10 ^ 6 Kerne). Der Code soll die Kommunikation zwischen (möglicherweise) verschiedenen Maschinen auf verschiedenen Architekturen ermöglichen. Er hat einen Kommentar geschrieben, der etwas in der Art sagt:

Wir würden normalerweise newund verwenden delete, aber hier verwende ich mallocund free. Dies ist erforderlich, da einige Compiler die Daten bei newVerwendung unterschiedlich auffüllen , was zu Fehlern bei der Datenübertragung zwischen verschiedenen Plattformen führt. Das passiert nicht mitmalloc .

Dies passt zu nichts, was ich vom Standard kenne new vs mallocFragen.

Was ist der Unterschied zwischen new / delete und malloc / free?Hinweise auf die Idee, dass der Compiler die Größe eines Objekts anders berechnen könnte (aber warum unterscheidet sich das dann von der Verwendung sizeof?).

malloc & platzierung neu gegen neu ist eine ziemlich beliebte Frage, spricht aber nur über die newVerwendung von Konstruktoren, wo mallocdies nicht der Fall ist, was für diese nicht relevant ist.

Wie versteht malloc die Ausrichtung? sagt, dass das Gedächtnis garantiert richtig ausgerichtet ist mit entweder newoder mallocwas ich vorher gedacht hatte.

Ich vermute, dass er irgendwann in der Vergangenheit seinen eigenen Fehler falsch diagnostiziert und daraus abgeleitet hat newund mallocunterschiedliche Mengen an Polsterung gegeben hat, was meiner Meinung nach wahrscheinlich nicht stimmt. Aber ich kann die Antwort bei Google oder in einer früheren Frage nicht finden.

Hilf mir, StackOverflow, du bist meine einzige Hoffnung!


33
+1 nur für die Erforschung verschiedener SO-Threads!
Iammilind

7
+1 Einfach einer der besten "Hilf mir selbst, bevor ich andere frage" -Forschungsjobs, die ich seit langer Zeit bei SO gesehen habe. Ich wünschte, ich könnte dies noch ein paar Mal verbessern.
WhozCraig

1
Geht der Übertragungscode davon aus, dass die Daten auf eine bestimmte Weise ausgerichtet sind, z. B. dass sie an einer 8-Byte-Grenze beginnen? Dies kann sich unterscheiden mallocund new, wie newin einigen Umgebungen, einen Block zuweisen, einige Daten am Anfang hinzufügen und einen Zeiger auf eine Position direkt nach diesen Daten zurückgeben. (Ich stimme den anderen innerhalb des Datenblocks zu mallocund newmuss dieselbe Art von Polsterung verwenden.)
Lindydancer

1
Wow, ich hatte nicht erwartet, dass diese Frage so beliebt ist! @ Lindydancer, ich glaube nicht, dass eine 8-Byte-Grenze angenommen wird. Interessanter Punkt.
Carver

1
Ein Grund, eine Zuordnungsmethode gegenüber einer anderen zu verwenden, besteht darin, dass "jemand anderes" die Freigabe des Objekts vornimmt. Wenn dieser "jemand anderes" das Objekt mit free löscht, müssen Sie es mit malloc zuweisen. (Ein Pad-Problem ist ein roter Hering.)
Lindydancer

Antworten:


25

IIRC gibt es einen wählerischen Punkt. mallocwird garantiert eine Adresse zurückgeben, die für jeden Standardtyp ausgerichtet ist. ::operator new(n)Es wird nur garantiert, dass eine Adresse zurückgegeben wird, die für einen Standardtyp ausgerichtet ist, der nicht größer als n ist. Wenn Tes sich nicht um einen Zeichentyp new T[n]handelt, muss nur eine Adresse zurückgegeben werden, die für ausgerichtet istT .

Dies ist jedoch nur relevant, wenn Sie implementierungsspezifische Tricks spielen, z. B. die unteren paar Bits eines Zeigers zum Speichern von Flags verwenden oder sich auf andere Weise darauf verlassen, dass die Adresse mehr Ausrichtung aufweist, als sie unbedingt benötigt.

Es hat keinen Einfluss auf das Auffüllen innerhalb des Objekts, das notwendigerweise genau das gleiche Layout hat, unabhängig davon, wie Sie den Speicher zugewiesen haben, den es belegt. Es ist daher schwer zu erkennen, wie der Unterschied zu Fehlern bei der Datenübertragung führen kann.

Gibt es Anzeichen dafür, was der Autor dieses Kommentars über Objekte auf dem Stapel oder in Globals denkt, ob sie seiner Meinung nach "wie Malloc gepolstert" oder "wie neu gepolstert" sind? Das könnte Hinweise darauf geben, woher die Idee kam.

Vielleicht ist er verwirrt, aber vielleicht der Code er spricht ist mehr als eine gerade Unterschied zwischen malloc(sizeof(Foo) * n)vs new Foo[n]. Vielleicht ist es eher wie:

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

vs.

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

Das heißt, vielleicht sagt er "Ich benutze Malloc", bedeutet aber "Ich packe die Daten manuell an nicht ausgerichteten Orten, anstatt eine Struktur zu verwenden". Eigentlich mallocist es nicht erforderlich, um die Struktur manuell zu packen, aber nicht zu erkennen, dass dies ein geringeres Maß an Verwirrung ist. Es ist erforderlich, das über das Kabel gesendete Datenlayout zu definieren. Verschiedene Implementierungen füllen die Daten unterschiedlich auf, wenn die Struktur verwendet wird.


Vielen Dank für die Punkte zur Ausrichtung. Bei den fraglichen Daten handelt es sich um ein char-Array, daher vermute ich, dass es sich hier weder um eine Ausrichtung noch um eine Struktur handelt - obwohl dies auch mein erster Gedanke war.
Carver

5
@Hbcdev: Nun, charArrays werden überhaupt nicht aufgefüllt, also bleibe ich bei "verwirrt" als Erklärung.
Steve Jessop

5

Möglicherweise hat Ihr Kollege an das new[]/delete[]magische Cookie gedacht (dies sind die Informationen, die die Implementierung beim Löschen eines Arrays verwendet). Dies wäre jedoch kein Problem gewesen, wenn die Zuordnung, die an der von zurückgegebenen Adresse beginnt, new[]verwendet worden wäre (im Gegensatz zu der des Zuweisers).

Verpackung scheint wahrscheinlicher. Variationen in ABIs können (zum Beispiel) dazu führen, dass am Ende einer Struktur eine andere Anzahl von nachfolgenden Bytes hinzugefügt wird (dies wird durch die Ausrichtung beeinflusst, berücksichtigen Sie auch Arrays). Mit malloc könnte die Position einer Struktur spezifiziert und somit leichter auf ein ausländisches ABI portiert werden. Diese Abweichungen werden normalerweise verhindert, indem die Ausrichtung und Packung der Übertragungsstrukturen festgelegt werden.


2
Dies war, was ich zuerst dachte, das Problem "Struktur ist größer als die Summe ihrer Teile". Vielleicht ist dies der Ursprung seiner Idee.
Carver

3

Das Layout eines Objekts kann nicht davon abhängen, ob es mit mallocoder zugewiesen wurde new. Beide geben dieselbe Art von Zeiger zurück, und wenn Sie diesen Zeiger an andere Funktionen übergeben, wissen sie nicht, wie das Objekt zugewiesen wurde. sizeof *ptrist nur abhängig von der Erklärung vonptr , nicht davon, wie sie vergeben wurde.


3

Ich glaube, Du hast recht. Das Auffüllen erfolgt durch den Compiler nicht newoder malloc. Padding-Überlegungen gelten auch dann, wenn Sie ein Array oder eine Struktur ohne Verwendung newoder mallocüberhaupt deklariert haben . Obwohl ich sehen kann, wie unterschiedliche Implementierungen von newund mallocProbleme beim Portieren von Code zwischen Plattformen verursachen können, kann ich nicht erkennen, wie sie Probleme beim Übertragen von Daten zwischen Plattformen verursachen können.


Ich hatte vorher angenommen, dass Sie dies newals eine nette Verpackung betrachten könnten, mallocaber aus anderen Antworten geht hervor, dass dies nicht ganz richtig ist. Konsens scheint zu sein, dass die Polsterung bei beiden gleich sein sollte; Ich denke, das Problem mit der Übertragung von Daten zwischen Plattformen tritt nur auf, wenn Ihr Übertragungsmechanismus fehlerhaft ist :)
hcarver

0

Wenn ich das Layout meiner einfachen alten Datenstruktur mit MS Visual-Compilern steuern möchte, verwende ich diese #pragma pack(1). Ich nehme an, eine solche Precompiler-Direktive wird für die meisten Compiler unterstützt, wie zum Beispiel gcc .

Dies hat zur Folge, dass alle Felder der Strukturen ohne Leerzeichen hintereinander ausgerichtet werden.

Wenn die Plattform am anderen Ende dasselbe tut (dh ihre Datenaustauschstruktur mit einem Abstand von 1 kompiliert), passen die auf beiden Seiten abgerufenen Daten gut zusammen. Daher musste ich in C ++ nie mit Malloc spielen.

Im schlimmsten Fall hätte ich in Betracht gezogen, den neuen Operator zu überladen, da er einige knifflige Dinge ausführt, anstatt malloc direkt in C ++ zu verwenden.


In welchen Situationen möchten Sie das Datenstrukturlayout steuern? Nur neugierig.
Carver

Und kennt jemand Compiler, die unterstützen pragma packoder ähnliches? Mir ist klar, dass es nicht Teil des Standards sein wird.
Carver

gcc unterstützt es zum Beispiel. In welcher Situation brauchte ich das: Teilen von Binärdaten zwischen zwei verschiedenen Plattenformen: Teilen von Binärdaten zwischen Windows und PalmOS, zwischen Windows und Linux. Links über gcc: gcc.gnu.org/onlinedocs/gcc/Structure_002dPacking-Pragmas.html
Stephane Rolland

0

Dies ist meine wilde Vermutung, woher dieses Ding kommt. Wie Sie bereits erwähnt haben, liegt das Problem bei der Datenübertragung über MPI.

Persönlich implementiere ich für meine komplizierten Datenstrukturen, die ich über MPI senden / empfangen möchte, immer Serialisierungs- / Deserialisierungsmethoden, die das Ganze in / aus einem Array von Zeichen packen / entpacken. Aufgrund des Auffüllens wissen wir nun, dass diese Größe der Struktur größer sein kann als die Größe ihrer Elemente, und daher muss auch die ungepolsterte Größe der Datenstruktur berechnet werden, damit wir wissen, wie viele Bytes gesendet / empfangen werden.

Wenn Sie beispielsweise std::vector<Foo> Amit dieser Technik über MPI senden / empfangen möchten , ist es falsch anzunehmen, dass die Größe des resultierenden Zeichenarrays A.size()*sizeof(Foo)im Allgemeinen ist. Mit anderen Worten, jede Klasse, die Serialisierungs- / Deserialisierungsmethoden implementiert, sollte auch eine Methode implementieren, die die Größe des Arrays angibt (oder das Array noch besser in einem Container speichert). Dies könnte der Grund für einen Fehler sein. Auf die eine oder andere Weise hat dies jedoch nichts mit newvs zu tun, mallocwie in diesem Thread ausgeführt.


Das Kopieren in Char-Arrays kann problematisch sein - möglicherweise befinden sich einige Ihrer Kerne auf Little-Endian-Architekturen und einige auf Big-Endian-Architekturen (möglicherweise nicht wahrscheinlich, aber möglich). Sie müssten sie XDR-codieren oder so, aber Sie könnten einfach benutzerdefinierte MPI-Datentypen verwenden. Sie berücksichtigen leicht die Polsterung. Aber ich kann sehen, was Sie über die mögliche Ursache von Missverständnissen sagen - ich nenne das Problem "Struktur ist größer als die Summe ihrer Teile".
Carver

Ja, das Definieren von MPI-Datentypen ist eine andere / richtige Methode, um dies zu tun. Guter Punkt über Endianness. Obwohl ich wirklich bezweifle, dass dies bei tatsächlichen Clustern passieren würde. Wie auch immer, ich dachte, wenn sie die gleiche Strategie verfolgen, könnte dies zu Fehlern führen ...
mmirzadeh

0

In c ++: wird das new Schlüsselwort verwendet, um bestimmte Speicherbytes in Bezug auf eine Datenstruktur zuzuweisen. Sie haben beispielsweise eine Klasse oder Struktur definiert und möchten Speicher für das Objekt zuweisen.

myclass *my = new myclass();

oder

int *i = new int(2);

In allen Fällen benötigen Sie jedoch den definierten Datentyp (Klasse, Struktur, Union, Int, Char usw.), und es werden nur die Speicherbytes zugewiesen, die für das Objekt / die Variable erforderlich sind. (dh Vielfache dieses Datentyps).

Bei der malloc () -Methode können Sie jedoch beliebige Speicherbytes zuweisen, und Sie müssen den Datentyp nicht immer angeben. Hier können Sie es in wenigen Möglichkeiten von malloc () beobachten:

void *v = malloc(23);

oder

void *x = malloc(sizeof(int) * 23);

oder

char *c = (char*)malloc(sizeof(char)*35);

-1

malloc ist eine Art von Funktion und new ist eine Art von Datentyp in c ++ in c ++, wenn wir malloc verwenden, als wir müssen, und typecast verwenden sollten, sonst gibt Ihnen der Compiler einen Fehler und wenn wir einen neuen Datentyp für die Zuweisung von Speicher verwenden, brauchen wir ihn nicht zu typisieren


1
Ich denke, Sie sollten versuchen, Ihre Antwort ein wenig mehr zu argumentieren.
Carlo

Dies scheint nicht die Frage zu beantworten, ob sie verschiedene Dinge mit Polstern machen, was ich oben wirklich gefragt habe.
Carver
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.