2009-02-03 8 views
21

La mayoría de los programadores experimentados saben que la alineación de datos es importante para el rendimiento del programa. He visto que algunos programadores escribieron un programa que asigna un tamaño de memoria más grande de lo que necesitan, y usan el puntero alineado como comenzar. Me pregunto si debería hacer eso en mi programa, no tengo idea de si hay alguna garantía de alineación de la dirección devuelta por la nueva operación de C++. Así que escribí un pequeño programa para probar¿Hay alguna garantía de alineación del retorno de la dirección mediante la nueva operación de C++?

for(size_t i = 0; i < 100; ++i) { 
    char *p = new char[123]; 
    if(reinterpret_cast<size_t>(p) % 4) { 
     cout << "*"; 
     system("pause"); 
    } 
    cout << reinterpret_cast<void *>(p) << endl; 
} 
for(size_t i = 0; i < 100; ++i) { 
    short *p = new short[123]; 
    if(reinterpret_cast<size_t>(p) % 4) { 
     cout << "*"; 
     system("pause"); 
    } 
    cout << reinterpret_cast<void *>(p) << endl; 
} 
for(size_t i = 0; i < 100; ++i) { 
    float *p = new float[123]; 
    if(reinterpret_cast<size_t>(p) % 4) { 
     cout << "*"; 
     system("pause"); 
    } 
    cout << reinterpret_cast<void *>(p) << endl; 
} 
system("pause"); 

El compilador que estoy usando es Visual C++ Express 2008. Parece que todas las direcciones de la nueva operación regresado están alineados. Pero no estoy seguro. Entonces mi pregunta es: ¿hay alguna garantía? Si tienen garantía, no tengo que alinearme, si no, tengo que hacerlo.

Respuesta

19

La alineación tiene la siguiente garantía de la norma (3.7.3.1/2):

El puntero devuelto estarán adecuadamente alineadas de modo que se puede convertir en un puntero de cualquier tipo de objeto completo y luego se usa para acceder al objeto o matriz en el almacenamiento asignado (hasta el almacenamiento es explícitamente desasignado por una llamada a una función de desasignación correspondiente).

EDITAR: Gracias a timday para poner de relieve un bug en gcc/glibc, donde la garantía no se cumple.

EDIT 2: El comentario de Ben destaca un caso de borde intersting. Los requisitos en las rutinas de asignación son para aquellos proporcionados por la norma solamente. Si la aplicación tiene su propia versión, entonces no hay tal garantía en el resultado.

+0

En teoría. En la práctica, si está utilizando gcc + glibc y tipos de SSE en un sistema de 32 bits, tenga en cuenta http://gcc.gnu.org/bugzilla/show_bug.cgi?id=15795. – timday

+0

@timday: este problema con los tipos de SSE también se aplicaba a las versiones recientes de MSVC++ (por ejemplo, .NET 2003). No he probado en la última versión, pero sospecho que todavía es el caso. –

+0

El valor devuelto por la función de asignación de operador 'operator new []()', que se refiere a ese requisito, no es el mismo puntero que se obtiene del operador 'new []'. Esta respuesta es incorrecta –

4

El operador nuevo/nuevo [] de la plataforma devolverá los punteros con una alineación suficiente para que funcione bien con los tipos de datos básicos (doble, flotante, etc.). Al menos cualquier compilador razonable de C++ + tiempo de ejecución debería hacer eso.

Si tiene requisitos especiales de alineación como para SSE, entonces es probable que sea una buena idea usar funciones especiales alignment_malloc o enrollar las suyas propias.

4

¡Trabajé en un sistema en el que utilizaban la alineación para liberar un bit extraño para uso propio!

Utilizaron el bit impar para implementar un sistema de memoria virtual.

Cuando un puntero tenía el bit impar establecido lo usaban para indicar que apuntaba (menos el bit impar ) a la información para obtener los datos de la base de datos, no los datos en sí.

Pensé que esto era un poco desagradable de codificación, que era muy ingenioso por su propio bien.

, Tony

+2

Se llaman punteros etiquetados, y no son nada infrecuentes. Muchas implementaciones de lenguaje de programación usan este truco para diferenciar entre un puntero y un entero. – geocar

+1

Y el interfuncionamiento ARM lo usa; cuando corresponde, las direcciones de código del modo ARM son uniformes, las direcciones del modo de pulgar son impares. He visto una implementación de árbol AVL que utilizaba los dos bits inferiores para almacenar la diferencia de altura de los subárboles de un nodo.En sistemas limitados, rellene los bits de la bandera siempre que pueda :-) –

+0

En las versiones anteriores de MAC OS (Classic), utilizaban los 8 bits superiores para el administrador de memoria. Los ptr de 68000 solo tienen un máximo de 24 bits, mientras que la dirección registra 32 bits. – AnthonyLambert

7

A propósito del MS documentation menciona algo acerca de malloc/nuevas direcciones que regresan que son de 16 bytes alineados, pero a partir de la experimentación este no es el caso. Sucedí que necesitaba la alineación de 16 bytes para un proyecto (para acelerar las copias de memoria con un conjunto de instrucciones mejorado), al final recurrí a escribir mi propio asignador ...

+0

Siento tu dolor ... –

9

Ésta es una respuesta tardía pero sólo para aclarar la situación en Linux - en sistemas de 64 bits la memoria es siempre de 16 bytes alineados:

http://www.gnu.org/software/libc/manual/html_node/Aligned-Memory-Blocks.html

La dirección de un bloque que devuelve malloc o realloc en el sistema GNU es siempre un múltiplo de ocho (o dieciséis en sistemas de 64 bits).

El operador new llama malloc internamente (ver ./gcc/libstdc++-v3/libsupc++/new_op.cc) por lo que este se aplica a new también.

La implementación de malloc que es parte de la glibc básicamente define MALLOC_ALIGNMENT para ser 2*sizeof(size_t) y size_t es de 32 bits = 4byte y 64 bits = 8byte en un x86-32 y x86-64 sistema, respectivamente.

$ cat ./glibc-2.14/malloc/malloc.c: 
... 
#ifndef INTERNAL_SIZE_T 
#define INTERNAL_SIZE_T size_t 
#endif 
... 
#define SIZE_SZ    (sizeof(INTERNAL_SIZE_T)) 
... 
#ifndef MALLOC_ALIGNMENT 
#define MALLOC_ALIGNMENT  (2 * SIZE_SZ) 
#endif 
Cuestiones relacionadas