Malloc vs new-разные прокладки
я просматриваю чужой код на C++ для нашего проекта, который использует MPI для высокопроизводительных вычислений (10^5 - 10^6 ядер). Код предназначен для обеспечения связи между (потенциально) различными машинами на разных архитектурах. Он написал комментарий, который говорит что-то вроде:
мы обычно используем
new
иdelete
, но здесь я используюmalloc
иfree
. Это необходимо, потому что некоторые компиляторы будут заполнять данные по-разному когдаnew
используется, что приводит к ошибкам в передаче данных между различными платформами. Это не происходит сmalloc
.
это не соответствует ничему, что я знаю от standard new
vs malloc
вопросы.
в чем разница между new/delete и malloc/free? намекает на мысль, что компилятор не может вычислить размер объекта по-разному (но тогда почему это отличается от использования sizeof
?).
malloc & размещение новый против нового-довольно популярный вопрос, но только говорит о new
использование конструкторов, где malloc
нет, что не имеет отношения к этому.
как Маллок понимает выравнивание? говорит, что память гарантированно будет правильно выровнен с new
или malloc
это то, что я раньше думал.
Я думаю, что он неправильно диагностировал свою собственную ошибку некоторое время назад и сделал вывод, что new
и malloc
дайте различные количества прокладки, которые я думаю, вероятно, это не так. Но я не могу найти ответ с помощью Google или в любом предыдущем вопросе.
помоги мне, StackOverflow, ты моя единственная надежда!
8 ответов:
IIRC есть один придирчивый момент.
malloc
гарантированно возвращает адрес, выровненный для любого стандартного типа.::operator new(n)
гарантируется только возврат адреса, выровненного для любого стандартного типа не больше n, а еслиT
это не тип символа тогдаnew T[n]
требуется только для возврата адреса, выровненного дляT
.но это актуально только тогда, когда вы играете специфические для реализации трюки, такие как использование нижних нескольких бит указателя для хранения флаги, или иначе полагаясь на адрес, чтобы иметь больше выравнивания, чем это строго необходимо.
это не влияет на заполнение внутри объекта, который обязательно имеет точно такой же макет независимо от того, как вы выделили память, которую он занимает. Поэтому трудно понять, как разница может привести к ошибкам при передаче данных.
есть ли какой-либо признак того, что автор этого комментария думает об объектах в стеке или в глобалах, независимо от того, по его мнению, они " дополнены, как Танос" или "мягкий как новый"? Это может дать ключ к разгадке, откуда взялась идея.
может быть, он запутался, но, возможно, код, о котором он говорит, больше, чем прямая разница между
malloc(sizeof(Foo) * n)
vsnew Foo[n]
. Может быть, это больше похоже на:malloc((sizeof(int) + sizeof(char)) * n);
и
struct Foo { int a; char b; } new Foo[n];
то есть, может быть, он слова "я использую malloc", но означает "я вручную упаковываю данные в невыровненные места вместо использования структуры". На самом деле
malloc
is не требуется для того, чтобы вручную упаковать структуру, но не понимая, что это меньшая степень путаницы. Надо определить формат данных, передаваемых по сети. Различные реализации будут заполнять данные по-разному, когда struct это.
ваш коллега, возможно, имел
new[]/delete[]
волшебный cookie в виду (это информация, которую реализация использует при удалении массива). Однако это не было бы проблемой, если бы выделение начиналось с адреса, возвращенногоnew[]
были использованы (в отличие от распределителя).упаковка кажется более вероятным. Вариации в ABIs могут (например) привести к различному количеству конечных байтов, добавленных в конце структуры (на это влияет выравнивание, также рассмотрим массивы). С malloc, положение структуры смогло быть определено и таким образом более легко портативно к чужому ABI. Эти изменения обычно предотвращаются путем указания выравнивания и упаковки передаточных структур.
макет объекта не может зависеть от того, был ли он выделен с помощью
malloc
илиnew
. Они оба возвращают один и тот же указатель, и когда вы передаете этот указатель другим функциям, они не будут знать, как был выделен объект.sizeof *ptr
просто зависит от объявленияptr
, не так, как это было назначено.
Я думаю, что вы правы. Заполнение выполняется компилятором не
new
илиmalloc
. Соображения заполнения будут применяться, даже если вы объявили массив или структуру без использованияnew
илиmalloc
на всех. В любом случае, пока я вижу, как разные реализацииnew
иmalloc
может вызвать проблемы при переносе кода между платформами, я полностью не вижу, как они могут вызвать проблемы с передачей данных между платформами.
когда я хочу контролировать макет моей простой старой структуры данных, с помощью MS Visual compilers я использую
#pragma pack(1)
. Я полагаю, что такая директива precompiler поддерживается для большинства компиляторов, например gcc.это приводит к выравниванию всех полей структур друг за другом, без пустых пространств.
Если платформа на другом конце делает то же самое ( т. е. скомпилировала свою структуру обмена данными с дополнением 1), то данные восстановленные с обеих сторон justs хорошо подходит. Таким образом, мне никогда не приходилось играть с malloc в C++.
в худшем случае я бы рассмотрел перегрузку нового оператора, так как он выполняет некоторые сложные вещи, а не использует malloc непосредственно в C++.
Это я догадываюсь, откуда это идет. Как вы уже упоминали, проблема заключается в передаче данных через MPI.
лично для моих сложных структур данных, которые я хочу отправлять/получать через MPI, я всегда реализую методы сериализации/десериализации, которые упаковывают/распаковывают все это в/из массива символов. Теперь из-за заполнения мы знаем, что этот размер структуры может быть больше, чем размер ее членов, и поэтому также необходимо рассчитать неупакованный размер структуры данных, чтобы мы знали, сколько байтов отправляется / принимается.
например, если вы хотите отправить/получить
std::vector<Foo> A
над MPI с помощью указанной техники неверно предполагать, что размер результирующего массива символов равенA.size()*sizeof(Foo)
в целом. Другими словами, каждый класс, реализующий методы serialize/deserialize, должен также реализовать метод, который сообщает размер массива (или еще лучше хранить массив в контейнере). Это может стать причина за ошибкой. Так или иначе, но это не имеет никакого отношения кnew
vsmalloc
как указано в этой теме.
в c++:
new
ключевое слово используется для выделения некоторых конкретных байтов памяти по отношению к некоторой структуре данных. Например, вы определили некоторый класс или структура, и вы хотите выделить память для объекта.myclass *my = new myclass();
или
int *i = new int(2);
но во всех случаях вам нужен определенный тип данных (class, struct, union, int, char и т. д...) и только тот байт памяти будет выделен, который требуется для его объекта / переменной. (т. е. кратные этот тип данных).
но в случае метода malloc () вы можете выделить любые байты памяти, и вам не нужно указывать тип данных в любое время. Здесь вы можете наблюдать это в нескольких возможностях malloc ():
void *v = malloc(23);
или
void *x = malloc(sizeof(int) * 23);
или
char *c = (char*)malloc(sizeof(char)*35);