c++ - ¿Realmente tengo que preocuparme por la alineación cuando uso un nuevo operador de colocación?
g++ memory-alignment (5)
Leí esto ¿ Cuándo debería preocuparme por la alineación? pero todavía no sé si tengo que preocuparme por el puntero no alineado devuelto por un nuevo operador de ubicación, como en este ejemplo:
class A {
public:
long double a;
long long b;
A() : a(1.3), b(1234) {}
};
char buffer[64];
int main() {
// (buffer + 1) used intentionally to have wrong alignment
A* a = new (buffer + 1) A();
a->~A();
}
__alignof(A) == 4
, (buffer + 1)
no está alineado a 4
. Pero todo funciona bien - el ejemplo completo aquí: http://ideone.com/jBrk8
Si esto depende de la arquitectura, entonces estoy usando: linux / powerpc / g ++ 4.xx
[ACTUALIZACIÓN] Justo después de publicar esta pregunta, leí este artículo: http://virtrev.blogspot.de/2010/09/memory-alignment-theory-and-c-examples.html . Tal vez los únicos inconvenientes en mi caso sean las penalizaciones de rendimiento, me refiero a que el costo de acceso no alineado está más que alineado.
Cuando llama a la ubicación nueva en un búfer:
A *a = new (buf) A;
está invocando el void* operator new (std::size_t size, void* ptr) noexcept
como se define en:
c ++ 11
18.6.1.3 Formas de colocación [new.delete.placement]
Estas funciones están reservadas, un programa en C ++ puede no definir funciones que desplazan las versiones en la biblioteca estándar de C ++ (17.6.4). Las disposiciones de (3.7.4) no se aplican a estas formas de colocación reservada de operador nuevo y operador borrado.
void* operator new(std::size_t size, void* ptr) noexcept;
Devoluciones:ptr
.
Observaciones: Intencionalmente no realiza ninguna otra acción.
Las disposiciones de (3.7.4) incluyen que el puntero devuelto debe estar adecuadamente alineado, por lo que está bien para void* operator new (std::size_t size, void* ptr) noexcept
para devolver un puntero no alineado si se pasa uno. Esto Sin embargo, no te deja descolgar:
5.3.4 Nuevo [expr.new]
[14] Nota: cuando la función de asignación devuelve un valor distinto de nulo, debe ser un puntero a un bloque de almacenamiento en el que se ha reservado espacio para el objeto. Se supone que el bloque de almacenamiento está alineado apropiadamente y del tamaño solicitado.
Por lo tanto, si pasa el almacenamiento no alineado a una expresión nueva de la ubicación, está violando la suposición de que el almacenamiento está alineado y el resultado es UB.
De hecho, en su programa anterior, si reemplaza long long b
con __m128 b
(después de #include <xmmintrin.h>
), entonces el programa se ejecutará de forma predeterminada, como se esperaba.
Sí, todo depende de la arquitectura y, probablemente, también de los indicadores de optimización del compilador. Todo funcionará bien hasta que hagas A b = a;
o algún otro acceso aleatorio que se compila en algunas operaciones de movdqa
y tu programa se bloquea.
Solo porque todo parece funcionar no significa que realmente lo haga.
C ++ es una especificación que define lo que se requiere para trabajar. El compilador también puede hacer que no funcionen las cosas requeridas. Eso es lo que significa "comportamiento indefinido": cualquier cosa puede pasar, por lo que su código ya no es portátil.
C ++ no requiere que esto funcione. Entonces, si lleva su código a un compilador donde esto no funciona, ya no puede culpar a C ++; Es tu culpa el mal uso del lenguaje.
algunos procesadores definitivamente explotarán con esto (sparc por ejemplo), otros no se preocuparán en absoluto, otros serán lentos. C ++ supone que sabes lo que estás haciendo (o no), lo mismo que reinterpret_cast, etc.
un google rápido me dice que gcc en powerpc tiene una opción para decirle al compilador si el sistema no maneja los accesos no alineados.
Supongo que, en cualquier caso, los accesos no alineados en esa plataforma serán muy lentos y deberían evitarse tanto como sea posible.