c++ - Malloc vs nuevo-diferente relleno
new-operator padding (8)
Estoy revisando el código C ++ de otra persona para nuestro proyecto que usa MPI para computación de alto rendimiento (10 ^ 5 - 10 ^ 6 núcleos). El código está destinado a permitir las comunicaciones entre (potencialmente) diferentes máquinas en diferentes arquitecturas. Escribió un comentario que dice algo como:
Normalmente usamos
new
ydelete
, pero aquí estoy usandomalloc
yfree
. Esto es necesario porque algunos compiladores rellenarán los datos de forma diferente cuando se usenew
, lo que generará errores en la transferencia de datos entre diferentes plataformas. Esto no sucede conmalloc
.
Esto no encaja con nada que yo sepa de las preguntas estándar new
frente a malloc
.
¿Cuál es la diferencia entre new / delete y malloc / free? insinúa la idea de que el compilador podría calcular el tamaño de un objeto de manera diferente (pero ¿por qué difiere de usar sizeof
?).
malloc & placement new vs. new es una pregunta bastante popular, pero solo habla de new
constructores que usan malloc
, lo que no es relevante para esto.
¿Cómo entiende malloc la alineación? dice que se garantiza que la memoria se alineará adecuadamente con new
o malloc
que es lo que había pensado anteriormente.
Mi suposición es que ha diagnosticado erróneamente su propio error en algún momento del pasado y dedujo que los new
y malloc
dan diferentes cantidades de relleno, lo que probablemente no sea cierto. Pero no puedo encontrar la respuesta con Google ni en ninguna pregunta previa.
¡Ayúdame, StackOverflow, eres mi única esperanza!
Creo que tienes razón. El compilador realiza el relleno no como new
o malloc
. Las consideraciones de relleno se aplicarían incluso si declaraste una matriz o estructura sin usar ninguna o malloc
. En cualquier caso, mientras puedo ver cómo las diferentes implementaciones de new
y malloc
pueden causar problemas al portar código entre plataformas, no veo por completo cómo podrían causar problemas al transferir datos entre plataformas.
Cuando quiero controlar el diseño de mi antigua estructura de datos simple, con los compiladores de MS Visual utilizo #pragma pack(1)
. Supongo que una directiva precompiladora de este tipo es compatible con la mayoría de los compiladores, como por ejemplo, gcc .
Esto tiene la consecuencia de alinear todos los campos de las estructuras uno detrás del otro, sin espacios vacíos.
Si la plataforma en el otro extremo hace lo mismo (es decir, compiló su estructura de intercambio de datos con un relleno de 1), entonces los datos recuperados en ambos lados justs encajan bien. Por lo tanto, nunca tuve que jugar con malloc en C ++.
En el peor de los casos, habría considerado sobrecargar al nuevo operador para que realice algunas cosas complicadas, en lugar de usar malloc directamente en C ++.
El diseño de un objeto no puede depender de si fue asignado usando malloc
o new
. Ambos devuelven el mismo tipo de puntero, y cuando pasas este puntero a otras funciones, no sabrán cómo se asignó el objeto. sizeof *ptr
solo depende de la declaración de ptr
, no de cómo se asignó.
Es posible que su colega haya tenido en cuenta la new[]/delete[]
cookie mágica new[]/delete[]
(esta es la información que la implementación utiliza al eliminar una matriz). Sin embargo, esto no habría sido un problema si se usó la asignación que comienza en la dirección devuelta por new[]
(en comparación con el asignador).
El embalaje parece más probable. Las variaciones en ABI podrían (por ejemplo) dar como resultado un número diferente de bytes finales añadidos al final de una estructura (esto se ve influenciado por la alineación, también se consideran las matrices). Con malloc, la posición de una estructura podría especificarse y, por lo tanto, ser más fácil de transportar a un ABI extranjero. Estas variaciones normalmente se previenen al especificar la alineación y el empaque de las estructuras de transferencia.
Esta es mi loca suposición de dónde viene esto. Como mencionaste, el problema es con la transmisión de datos a través de MPI.
Personalmente, para mis complicadas estructuras de datos que deseo enviar / recibir a través de MPI, siempre implemento métodos de serialización / deserialización que empacan / descomponen todo en / desde una matriz de caracteres. Ahora, debido al relleno, sabemos que ese tamaño de la estructura podría ser mayor que el tamaño de sus miembros y, por lo tanto, uno también necesita calcular el tamaño no relleno de la estructura de datos para que sepamos cuántos bytes se están enviando / recibiendo.
Por ejemplo, si desea enviar / recibir std::vector<Foo> A
sobre MPI con dicha técnica, es incorrecto suponer que el tamaño de la matriz de caracteres resultante es A.size()*sizeof(Foo)
en general. En otras palabras, cada clase que implemente métodos de serialización / deserialización también debería implementar un método que informe el tamaño de la matriz (o mejor aún, almacene la matriz en un contenedor). Esto podría ser la razón detrás de un error. De una manera u otra, sin embargo, eso no tiene nada que ver con el new
vs malloc
como se señala en este hilo.
IIRC hay un punto quisquilloso. malloc
garantiza devolver una dirección alineada para cualquier tipo estándar. ::operator new(n)
solo garantiza devolver una dirección alineada para cualquier tipo estándar no mayor que n , y si T
no es un tipo de carácter, entonces la new T[n]
solo es necesaria para devolver una dirección alineada para T
Pero esto solo es relevante cuando se utilizan trucos específicos de la implementación, como usar los últimos bits de un puntero para almacenar indicadores, o confiar en la dirección para tener más alineación de la que estrictamente necesita.
No afecta el relleno dentro del objeto, que necesariamente tiene exactamente el mismo diseño independientemente de cómo haya asignado la memoria que ocupa. Por lo tanto, es difícil ver cómo la diferencia podría provocar errores al transferir datos.
¿Hay alguna señal de lo que el autor de ese comentario piensa sobre los objetos en la pila o en los globales, ya sea en su opinión que están "acolchados como malloc" o "acolchados como nuevos"? Eso podría dar pistas sobre de dónde vino la idea.
Quizás esté confundido, pero tal vez el código del que habla es más que una diferencia directa entre malloc(sizeof(Foo) * n)
vs new Foo[n]
. Tal vez es más como:
malloc((sizeof(int) + sizeof(char)) * n);
vs.
struct Foo { int a; char b; }
new Foo[n];
Es decir, tal vez él está diciendo "uso malloc", pero significa "empacar manualmente los datos en ubicaciones desalineadas en lugar de usar una estructura". En realidad malloc
no es necesario para empaquetar manualmente la estructura, pero no se da cuenta de que existe un menor grado de confusión. Es necesario definir el diseño de datos enviado a través del cable. Las diferentes implementaciones rellenarán los datos de manera diferente cuando se use la estructura .
malloc es un tipo de función y nuevo es un tipo de tipo de datos en c ++ en c ++, si usamos malloc de lo que debemos y deberíamos usar typecast; de lo contrario, el compilador dará error y si usamos nuevo tipo de datos para asignación de memoria que no necesitamos a encasillar
En c ++: new
palabra clave se utiliza para asignar algunos bytes de memoria particulares con respecto a cierta estructura de datos. Por ejemplo, ha definido alguna clase o estructura y desea asignar memoria para su objeto.
myclass *my = new myclass();
o
int *i = new int(2);
Pero en todos los casos necesita el tipo de datos definido (clase, estructura, unión, int, char, etc.) y solo se asignarán los bytes de memoria necesarios para su objeto / variable. (es decir, múltiplos de ese tipo de datos).
Pero en el caso del método malloc (), puede asignar cualquier byte de memoria y no necesita especificar el tipo de datos en todo momento. Aquí puedes observarlo en pocas posibilidades de malloc ():
void *v = malloc(23);
o
void *x = malloc(sizeof(int) * 23);
o
char *c = (char*)malloc(sizeof(char)*35);