2009-02-03 8 views
21

La maggior parte dei programmatori esperti sa che l'allineamento dei dati è importante per le prestazioni del programma. Ho visto alcuni programmi scritti da programmatori che allocano una dimensione di buffer più grande del necessario e utilizzano il puntatore allineato come inizio. Mi chiedo se dovessi farlo nel mio programma, non ho idea di alcuna garanzia di allineamento dell'indirizzo restituito dalla nuova operazione di C++. Così ho scritto un piccolo programma per testareEsiste una garanzia di allineamento dell'indirizzo restituito dalla nuova operazione di 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"); 

Il compilatore che sto usando è Visual C++ espresso 2008. Sembra che tutti gli indirizzi della nuova operazione restituita siano allineati. Ma non sono sicuro. Quindi la mia domanda è: c'è qualche garanzia? Se hanno garanzia, non devo allinearmi, se no, devo.

risposta

19

L'allineamento ha la seguente garanzia dallo standard (3.7.3.1/2):

Il puntatore restituito deve essere opportunamente posizionato in modo che può essere convertito in un puntatore di qualsiasi tipo di oggetto completo e quindi utilizzato per accedere all'oggetto o alla matrice nella memoria allocata (fino a l'archiviazione viene esplicitamente rilasciata da una chiamata a una funzione di deallocazione corrispondente).

EDIT: Grazie a timday per evidenziare un bug in gcc/glibc in cui la garanzia non regge.

MODIFICA 2: il commento di Ben evidenzia un caso di bordo intersecante. I requisiti per le routine di allocazione sono solo quelli forniti dallo standard. Se l'applicazione ha la propria versione, non c'è alcuna garanzia sul risultato.

+0

In teoria. In pratica, se si utilizzano i tipi gcc + glibc e SSE su un sistema a 32 bit, tenere presente http://gcc.gnu.org/bugzilla/show_bug.cgi?id=15795. – timday

+0

@timday: questo problema con i tipi SSE era valido anche per le versioni recenti di MSVC++ (ad esempio .NET 2003). Non ho provato l'ultima versione, ma ho il sospetto che sia ancora così. –

+0

Il valore restituito dalla funzione di allocazione helper 'operator new []()', che riguarda tale requisito, non è lo stesso puntatore che si ottiene dall'operatore 'new []'. Questa risposta è sbagliata. –

4

nuovo operatore nuovo/[] della piattaforma tornerà puntatori con allineamento sufficiente in modo che si esibiranno bene con tipi di dati di base (doppie, galleggiante, ecc.). Almeno qualsiasi ragionevole compilatore C++ + runtime dovrebbe farlo.

Se si dispone di requisiti di allineamento speciali come per SSE, allora è probabilmente una buona idea utilizzare speciali funzioni align_malloc o eseguire il rollover.

4

ho lavorato su un sistema in cui hanno usato l'allineamento per liberare un po 'strano perché non proprio uso!

hanno usato il po 'strano di implementare un sistema di memoria virtuale.

Quando un puntatore ha impostato il bit dispari, ha usato quello per indicare che puntava (meno il bit dispari ) alle informazioni per ottenere i dati dal database non i dati stessi.

ho pensato che questo un particolarmente brutto po 'di codifica che è stato lontano per intelligente per il suo bene !!

Tony

+2

Sono definiti puntatori etichettati e non sono affatto rari. Un sacco di implementazioni del linguaggio di programmazione utilizzano questo trucco per distinguere tra un puntatore e un intero. – geocar

+1

E l'interazione tra ARM lo utilizza - laddove applicabile, gli indirizzi di codice in modalità ARM sono pari, gli indirizzi in modalità pollice sono dispari. Ho visto un'implementazione di albero AVL che utilizzava i due bit inferiori per memorizzare la differenza di altezza dei sottostrutture di un nodo.Sui sistemi limitati, i bit di flag sono sempre disponibili ovunque :-) –

+0

Nelle prime versioni di MAC OS (Classic) utilizzavano i primi 8 bit per il gestore di memoria. I ptr del 68000 dove solo un massimo di 24 bit mentre l'indirizzo registra dove 32 bit. – AnthonyLambert

7

inciso il MS documentation accenna qualcosa circa malloc/nuovi indirizzi di ritorno, che sono di 16 byte allineati, ma dalla sperimentazione non è questo il caso. Mi è capitato di avere bisogno di allineamento a 16 byte per un progetto (per velocizzare le copie di memoria con set di istruzioni migliorato), alla fine ho fatto ricorso a scrivere il mio allocatore ...

+0

Sento il tuo dolore ... –

9

Questa è una risposta in ritardo, ma solo per chiarire la situazione su Linux - su sistemi a 64-bit memoria è sempre di 16 byte allineato:

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

L'indirizzo di un blocco restituito da malloc o realloc nel sistema GNU è sempre uno multiplo di otto (o sedici su sistemi a 64 bit).

L'operatore new chiama malloc internamente (vedi ./gcc/libstdc++-v3/libsupc++/new_op.cc) così questo vale per new pure.

L'attuazione di malloc che fa parte del glibc definisce sostanzialmente MALLOC_ALIGNMENT di essere 2*sizeof(size_t) e size_t è 32bit = 4byte e 64 bit = 8byte su un x86-32 e x86-64 sistema, rispettivamente.

$ 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