Sprawdzam czyjś kod C ++ dla naszego projektu, który używa MPI do obliczeń o wysokiej wydajności (10 ^ 5 - 10 ^ 6 rdzeni). Kod ma umożliwić komunikację między (potencjalnie) różnymi maszynami na różnych architekturach. Napisał komentarz, który mówi coś w rodzaju:
Zwykle używamy
new
idelete
, ale tutaj używammalloc
ifree
. Jest to konieczne, ponieważ niektóre kompilatory będą wypełniać dane inaczej, gdynew
są używane, co prowadzi do błędów w przesyłaniu danych między różnymi platformami. To się nie dzieje zmalloc
.
To nie pasuje do niczego, co znam z pytań standardowych new
i malloc
pytań.
Jaka jest różnica między new / delete i malloc / free? podpowiada, że kompilator może inaczej obliczyć rozmiar obiektu (ale dlaczego różni się to od używania sizeof
?).
malloc i umieszczenie nowe kontra nowe to dość popularne pytanie, ale mówi tylko o new
używaniu konstruktorów tam, gdzie malloc
nie, co nie ma tu znaczenia.
jak Malloc rozumie wyrównanie? mówi, że pamięć jest na pewno odpowiednio wyrównana z jednym new
lub z malloc
tym, co wcześniej myślałem.
Domyślam się, że jakiś czas w przeszłości błędnie zdiagnozował swój własny błąd i wydedukował to new
imalloc
podał różne ilości wypełnienia, co moim zdaniem prawdopodobnie nie jest prawdą. Ale nie mogę znaleźć odpowiedzi w Google ani w żadnym poprzednim pytaniu.
Pomóż mi, StackOverflow, jesteś moją jedyną nadzieją!
źródło
malloc
inew
, ponieważnew
w niektórych środowiskach przydziela się blok, dodaje pewne dane na początku i zwraca wskaźnik do lokalizacji tuż po tych danych. (Zgadzam się z innymi, wewnątrz bloku danychmalloc
inew
muszę użyć tego samego rodzaju wypełnienia.)Odpowiedzi:
IIRC jest jeden wybredny punkt.
malloc
gwarantuje zwrócenie adresu wyrównanego dla dowolnego typu standardowego.::operator new(n)
gwarantuje tylko zwrócenie adresu wyrównanego dla dowolnego typu standardowego nie większego niż n , a jeśliT
nie jest typem znakowym,new T[n]
wymagane jest tylko zwrócenie adresu wyrównanego dlaT
.Jest to jednak istotne tylko wtedy, gdy wykonujesz sztuczki specyficzne dla implementacji, takie jak używanie kilku dolnych bitów wskaźnika do przechowywania flag lub w inny sposób poleganie na adresie, aby uzyskać więcej wyrównania niż jest to ściśle potrzebne.
Nie wpływa na wypełnienie w obiekcie, który z konieczności ma dokładnie ten sam układ, niezależnie od tego, jak przydzielono zajmowaną przez niego pamięć. Trudno więc zrozumieć, w jaki sposób różnica może spowodować błędy w przesyłaniu danych.
Czy jest jakiś znak, co autor tego komentarza myśli o obiektach na stosie lub w globach, czy jego zdaniem są one „wyściełane jak malloc”, czy „wyściełane jak nowe”? To może dać wskazówki, skąd wziął się pomysł.
Może on mylić, ale może kod on mówi jest czymś więcej niż prostą różnicę między
malloc(sizeof(Foo) * n)
vsnew Foo[n]
. Może bardziej przypomina:vs.
To znaczy, może mówi „używam malloc”, ale ma na myśli „ręcznie pakuję dane w niewyrównane lokalizacje zamiast używać struktury”. W rzeczywistości
malloc
nie jest potrzebne do ręcznego pakowania struktury, ale nie zdawanie sobie sprawy, że jest to mniejszy stopień zamieszania. Konieczne jest zdefiniowanie układu danych przesyłanych przewodem. Różne implementacje będą wypełniać dane inaczej, gdy używana jest struktura .źródło
char
tablice nigdy nie są w ogóle uzupełniane , więc będę trzymał się wyjaśnienia „zdezorientowany”.Twój kolega mógł mieć
new[]/delete[]
na myśli magiczne ciasteczko (są to informacje używane przez implementację podczas usuwania tablicy). Nie stanowiłoby to jednak problemu, gdyby zastosowano alokację rozpoczynającą się od adresu zwróconego przeznew[]
(w przeciwieństwie do alokatora).Bardziej prawdopodobne wydaje się zapakowanie . Różnice w ABI mogą (na przykład) skutkować różną liczbą końcowych bajtów dodanych na końcu struktury (ma na to wpływ wyrównanie, należy również wziąć pod uwagę tablice). Dzięki malloc położenie konstrukcji można określić, a tym samym łatwiej przenieść do obcego ABI. Zmiennościom tym zwykle zapobiega się przez określenie wyrównania i upakowania struktur transferowych.
źródło
Układ obiektu nie może zależeć od tego, czy został przydzielony za pomocą,
malloc
czynew
. Oba zwracają ten sam rodzaj wskaźnika, a gdy przekażesz ten wskaźnik do innych funkcji, nie będą wiedzieć, w jaki sposób obiekt został przydzielony.sizeof *ptr
zależy tylko od deklaracjiptr
, a nie od tego , jak została przypisana.źródło
Myślę, że masz rację. Wypełnienie jest wykonywane przez kompilator nie
new
lubmalloc
. Zagadnienia dotyczące wypełnienia będą miały zastosowanie nawet wtedy, gdy zadeklarujesz tablicę lub strukturę bez użycianew
lubmalloc
w ogóle. W każdym razie, chociaż widzę, jak różne implementacjenew
imalloc
mogą powodować problemy podczas przenoszenia kodu między platformami, całkowicie nie widzę, jak mogą powodować problemy z przesyłaniem danych między platformami.źródło
new
za fajne opakowanie,malloc
ale z innych odpowiedzi wydaje się, że nie jest to do końca prawdą. Wydaje się, że konsensus jest taki, że wypełnienie powinno być takie samo w przypadku obu; Myślę, że problem z przesyłaniem danych między platformami pojawia się tylko wtedy, gdy twój mechanizm transferu jest uszkodzony :)Kiedy chcę kontrolować układ mojej zwykłej starej struktury danych, używam kompilatorów MS Visual
#pragma pack(1)
. Przypuszczam, że taka dyrektywa prekompilatora jest obsługiwana przez większość kompilatorów, takich jak na przykład gcc .Skutkuje to wyrównaniem wszystkich pól struktur jedno za drugim, bez pustych przestrzeni.
Jeśli platforma na drugim końcu robi to samo (tj. Skompilowała swoją strukturę wymiany danych z wypełnieniem 1), to dane odzyskane po obu stronach pasują po prostu dobrze. Dlatego nigdy nie musiałem bawić się mallocem w C ++.
W najgorszym przypadku rozważałbym przeciążenie nowego operatora, ponieważ wykonuje on kilka trudnych rzeczy, zamiast używać malloc bezpośrednio w C ++.
źródło
pragma pack
lub podobnych? Zdaję sobie sprawę, że nie będzie to częścią standardu.To jest moje dzikie przypuszczenie, skąd pochodzi ta rzecz. Jak wspomniałeś, problem dotyczy transmisji danych przez MPI.
Osobiście, dla moich skomplikowanych struktur danych, które chcę wysyłać / odbierać przez MPI, zawsze implementuję metody serializacji / deserializacji, które pakują / rozpakowują całość do / z tablicy znaków. Teraz, dzięki wypełnieniu, wiemy, że ten rozmiar struktury może być większy niż rozmiar jej elementów, a zatem należy również obliczyć niezabudowany rozmiar struktury danych, abyśmy wiedzieli, ile bajtów jest wysyłanych / odbieranych.
Na przykład, jeśli chcesz wysyłać / odbierać
std::vector<Foo> A
przez MPI tą techniką, błędem jest zakładanie, że rozmiar wynikowej tablicy znaków jestA.size()*sizeof(Foo)
ogólnie. Innymi słowy, każda klasa, która implementuje metody serializacji / deserializacji, powinna również implementować metodę, która zgłasza rozmiar tablicy (lub jeszcze lepiej przechowywać tablicę w kontenerze). To może stać się przyczyną błędu. Tak czy inaczej, nie ma to jednak nic wspólnego znew
vs,malloc
jak wskazano w tym wątku.źródło
W języku c ++:
new
słowo kluczowe służy do przydzielania określonych bajtów pamięci w odniesieniu do jakiejś struktury danych. Na przykład zdefiniowałeś jakąś klasę lub strukturę i chcesz przydzielić pamięć dla jej obiektu.lub
Ale we wszystkich przypadkach potrzebujesz zdefiniowanego typu danych (class, struct, union, int, char itp.) I przydzielone zostaną tylko te bajty pamięci, które są wymagane dla obiektu / zmiennej. (tj. wielokrotności tego typu danych).
Ale w przypadku metody malloc () możesz przydzielić dowolne bajty pamięci i nie musisz cały czas określać typu danych. Tutaj możesz to zaobserwować w kilku możliwościach malloc ():
lub
lub
źródło
malloc jest typem funkcji, a new jest typem danych w c ++ w c ++, jeśli używamy malloc niż musimy i powinniśmy używać typecast, w przeciwnym razie kompilator daje błąd i jeśli używamy nowego typu danych do alokacji pamięci, niż nie potrzebujemy na maszynie
źródło