Estou revisando o código C ++ de outra pessoa para nosso projeto que usa MPI para computação de alto desempenho (10 ^ 5 - 10 ^ 6 núcleos). O código se destina a permitir a comunicação entre (potencialmente) diferentes máquinas em diferentes arquiteturas. Ele escreveu um comentário que diz algo como:
Normalmente usaríamos
new
edelete
, mas aqui estou usandomalloc
efree
. Isso é necessário porque alguns compiladores preencherão os dados de maneira diferente quandonew
forem usados, levando a erros na transferência de dados entre plataformas diferentes. Isso não acontece commalloc
.
Isso não se encaixa com nada que eu conheço de perguntas padrão new
vs.malloc
Qual é a diferença entre new / delete e malloc / free? sugere a ideia de que o compilador poderia calcular o tamanho de um objeto de maneira diferente (mas então por que isso difere de usar sizeof
?).
malloc & placement new vs. new é uma questão bastante popular, mas só fala sobre o new
uso de construtores onde malloc
não, o que não é relevante para isso.
como o malloc entende o alinhamento? diz que a memória tem garantia de estar devidamente alinhada com um new
ou com o malloc
que eu pensava anteriormente.
Meu palpite é que ele diagnosticou erroneamente seu próprio bug algum tempo no passado e deduziu isso new
e malloc
deu diferentes quantidades de preenchimento, o que eu acho que provavelmente não é verdade. Mas não consigo encontrar a resposta no Google ou em nenhuma pergunta anterior.
Ajude-me, StackOverflow, você é minha única esperança!
fonte
malloc
enew
, comonew
em alguns ambientes alocar um bloco, adiciona alguns dados ao início e retorna um ponteiro para um local logo após esses dados. (Concordo com os outros, dentro do bloco de dados,malloc
enew
devo usar o mesmo tipo de preenchimento.)Respostas:
IIRC há um ponto exigente.
malloc
tem a garantia de retornar um endereço alinhado a qualquer tipo de padrão.::operator new(n)
só tem garantia de retornar um endereço alinhado para qualquer tipo padrão não maior que n , e seT
não for um tipo de caractere, entãonew T[n]
é necessário retornar apenas um endereço alinhado paraT
.Mas isso só é relevante quando você está jogando truques específicos de implementação, como usar os poucos bits inferiores de um ponteiro para armazenar sinalizadores ou depender do endereço para ter mais alinhamento do que o estritamente necessário.
Não afeta o preenchimento dentro do objeto, que necessariamente tem exatamente o mesmo layout, independentemente de como você alocou a memória que ocupa. Portanto, é difícil ver como a diferença pode resultar em erros na transferência de dados.
Há algum sinal do que o autor desse comentário pensa sobre objetos na pilha ou em globais, sejam em sua opinião "preenchidos como malloc" ou "preenchidos como novos"? Isso pode dar pistas sobre a origem da ideia.
Talvez ele está confuso, mas talvez o código que ele está falando é mais do que uma diferença clara entre
malloc(sizeof(Foo) * n)
vsnew Foo[n]
. Talvez seja mais como:vs.
Ou seja, talvez ele esteja dizendo "Eu uso malloc", mas significa "Eu empacoto manualmente os dados em locais não alinhados em vez de usar uma estrutura". Na verdade,
malloc
não é necessário para empacotar manualmente a estrutura, mas deixar de perceber isso é um grau menor de confusão. É necessário definir o layout dos dados enviados pela rede. Implementações diferentes preencherão os dados de maneira diferente quando a estrutura for usada.fonte
char
arrays nunca são preenchidos, então vou ficar com "confused" como explicação.Seu colega pode ter tido o
new[]/delete[]
"magic cookie" em mente (esta é a informação que a implementação usa ao deletar um array). No entanto, isso não teria sido um problema se a alocação começando no endereço retornado pornew[]
fosse usada (ao contrário do alocador).Fazer as malas parece mais provável. Variações em ABIs podem (por exemplo) resultar em um número diferente de bytes finais adicionados ao final de uma estrutura (isso é influenciado pelo alinhamento, considere também matrizes). Com o malloc, a posição de uma estrutura pode ser especificada e, portanto, mais facilmente transportável para uma ABI estrangeira. Essas variações são normalmente evitadas especificando o alinhamento e o empacotamento das estruturas de transferência.
fonte
O layout de um objeto não pode depender se ele foi alocado usando
malloc
ounew
. Ambos retornam o mesmo tipo de ponteiro, e quando você passa esse ponteiro para outras funções, eles não saberão como o objeto foi alocado.sizeof *ptr
depende apenas da declaração deptr
, não de como foi atribuído.fonte
Eu acho que você está certo. O preenchimento é feito pelo compilador não
new
oumalloc
. As considerações de preenchimento se aplicam mesmo se você declarar uma matriz ou estrutura sem usarnew
oumalloc
em tudo. Em qualquer caso, embora eu possa ver como diferentes implementações denew
emalloc
podem causar problemas ao portar código entre plataformas, não consigo ver como eles podem causar problemas de transferência de dados entre plataformas.fonte
new
um bom invólucro para,malloc
mas parece que, a partir de outras respostas, isso não é totalmente verdade. O consenso parece ser que o preenchimento deve ser o mesmo com qualquer um; Acho que o problema com a transferência de dados entre plataformas só surge se o seu mecanismo de transferência estiver com defeito :)Quando eu quero controlar o layout da minha estrutura de dados simples e antiga, eu uso os compiladores MS Visual
#pragma pack(1)
. Suponho que essa diretiva de pré-compilador seja compatível com a maioria dos compiladores, como, por exemplo, gcc .Isso tem como consequência o alinhamento de todos os campos das estruturas um atrás do outro, sem espaços vazios.
Se a plataforma na outra extremidade fizer o mesmo (isto é, compilou sua estrutura de troca de dados com um preenchimento de 1), então os dados recuperados em ambos os lados se ajustam bem. Portanto, nunca precisei brincar com malloc em C ++.
Na pior das hipóteses, eu teria considerado sobrecarregar o novo operador para que ele execute algumas coisas complicadas, em vez de usar malloc diretamente em C ++.
fonte
pragma pack
ou semelhantes? Sei que não fará parte do padrão.Este é o meu palpite de onde esta coisa está vindo. Como você mencionou, o problema é com a transmissão de dados por MPI.
Pessoalmente, para minhas estruturas de dados complicadas que desejo enviar / receber por MPI, sempre implemento métodos de serialização / desserialização que empacotam / descompactam tudo em / de uma matriz de caracteres. Agora, devido ao preenchimento, sabemos que esse tamanho da estrutura pode ser maior do que o tamanho de seus membros e, portanto, também é necessário calcular o tamanho não preenchido da estrutura de dados para sabermos quantos bytes estão sendo enviados / recebidos.
Por exemplo, se você deseja enviar / receber
std::vector<Foo> A
através de MPI com a referida técnica, é errado assumir que o tamanho do array resultante de caracteres éA.size()*sizeof(Foo)
em geral. Em outras palavras, cada classe que implementa métodos de serializar / desserializar também deve implementar um método que relata o tamanho do array (ou melhor ainda, armazena o array em um contêiner). Essa pode ser a razão por trás de um bug. De uma forma ou de outra, no entanto, isso não tem nada a ver comnew
vs,malloc
conforme apontado neste tópico.fonte
Em c ++: a
new
palavra - chave é usada para alocar alguns bytes específicos de memória com relação a alguma estrutura de dados. Por exemplo, você definiu alguma classe ou estrutura e deseja alocar memória para seu objeto.ou
Mas em todos os casos você precisa do tipo de dados definido (classe, estrutura, união, int, char, etc ...) e apenas os bytes de memória que são necessários para seu objeto / variável serão alocados. (ou seja, múltiplos desse tipo de dados).
Mas no caso do método malloc (), você pode alocar quaisquer bytes de memória e não precisa especificar o tipo de dados o tempo todo. Aqui você pode observá-lo em algumas possibilidades de malloc ():
ou
ou
fonte
malloc é um tipo de função e new é um tipo de tipo de dados em c ++ em c ++, se usarmos malloc do que devemos e devemos usar typecast, caso contrário o compilador dá erro e se usarmos um novo tipo de dados para alocação de memória, não precisamos moldar
fonte