2014-09-10 10 views
8

Il mio programma è conforme alla regola di aliasing rigorosa, ad eccezione di un posto: un'unità di compilazione che contiene funzioni di hashing come MurmurHash3, SpookyHash, ecc. Su x86 e x86_64, queste funzioni di hashing accettano uno const char *, li inoltrano a uint32 e processano dati in blocchi di 4 byte. Questo li rende molto più veloci rispetto ai dati di elaborazione byte per byte, ma credo che ciò infrange la rigida regola dell'aliasing. In questo momento, compilo questa compilation con -fno-strict-aliasing, mentre compilo il resto del programma con -fstrict-aliasing.È possibile abilitare l'ottimizzazione link-time mentre si disabilita solo l'aliasing rigoroso per alcune funzioni?

Ma mi chiedo cosa succede se abilito l'ottimizzazione link-time. Per quanto ne so, GCC e Clang implementano l'ordinamento di ottimizzazione del link-time memorizzando la sorgente del programma in file .o, in modo che nella fase di collegamento il compilatore conosca il codice sorgente dell'intero programma. È quindi ancora possibile disabilitare il rigoroso aliasing solo per le funzioni di hashing? O ora devo disabilitare il rigoroso aliasing per l'intero programma? O ho frainteso completamente le cose, e MurmurHash3/SpookyHash sono in effetti rigorosi aliasing conformi?

+8

Perché non si può semplicemente utilizzare 'memcpy' per copiare i byte in una variabile locale di tipo' uint32', e quindi operare su quella variabile locale? Questo dovrebbe essere ottimizzato per l'equivalente del tuo codice, ma senza violare alcuna regola di aliasing. – hvd

+0

Non ero sicuro se il compilatore sarebbe stato in grado di ottimizzare questo. Se lo fa, sarebbe fantastico. Ma vedendo la risposta di Christoph, sembrerebbe che il compilatore non sia in grado di ottimizzarlo. – Hongli

+0

La risposta di Christoph contiene un collegamento al codice e quel codice non fa ciò che ho descritto. Quando si vogliono trattare i buffer di varie dimensioni come serie di valori 'uint64_t', quel codice copia l'intero buffer in una matrice di' uint64_t' (come parte di 'union'), e quello che sto descrivendo è avere un singolo 'uint64_t' variabile e copia ripetutamente gli 8 byte specifici che vuoi leggere. – hvd

risposta

3

Dovrebbe essere possibile (almeno si potrebbe provare) dal GCC recente fornisce function specific option pragmas. Si potrebbe provare ad aggiungere qualcosa di simile

#pragma GCC optimize ("-fstrict-aliasing") 

prima le funzioni di aliasing, e mettere

#pragma GCC reset_options 

dopo di loro.

forse avete bisogno di avvolgere questi con

#if __GNUC__ >= 4 

e naturalmente alcuni #endif

In alternativa, trucchi uso Builder (per esempio autoconf, cmake, o un sofisticato GNU make 4.0 regola, ecc ...) per definire il proprio HAVE_GENUINE_GCC come 1 solo per il compilatore originale GCC e il proprio HAVE_GENUINE_CLANG come 1 per il compilatore originale Clang/LLVM, ecc. O forse rilevare che i pragma precedenti sono compresi su alcuni codici di esempio e quindi definire HAVE_WORKING_PRAGMA_GCC_OPTIMIZE come 1.

proposito, il GCC almeno, -flto è non memorizzazione in oggetto file una rappresentazione della sorgente di programma, ma solo una forma digerita di alcune rappresentazioni interne GCC (come Gimple, ecc ...) ottenuto durante la compilazione del codice sorgente. Questo è abbastanza diverso!

PS. Non ci ho provato, quindi forse non è così semplice.

+0

Purtroppo Clang non supporta questo. Il mio software è utilizzato dagli utenti su vari sistemi operativi. Alcuni usano Clang (ad esempio OS X e FreeBSD). – Hongli

+0

Potresti giocare a build trucchi (ad esempio con 'autoconf' o' cmake' ...) per definire la tua macro 'HAVE_GNU_GCC' come 1 per il GCC originale .... –

6

Ci sono tre cose da prendere in considerazione:

  • prestazioni
  • portabilità
  • conformità agli standard

Otterrete migliori prestazioni se si evita la copia dei dati e può garantire l'accesso in linea .

L'accesso non allineato e l'aliasing sono problemi di portabilità. Se decidi di copiare i dati, questo si occuperà di entrambi.In caso contrario, è necessario regolare l'algoritmo per gestire i dati di input non allineati e garantire che non vi siano accessi concorrenti tramite puntatori di tipo incompatibile:

Se si accede ai dati solo attraverso un singolo tipo di puntatore, la violazione delle regole di digitazione effettive renderà il tuo programma non conforme, ma probabilmente non sarà un problema nella pratica, anche se non passi -fno-strict-aliasing - che è dove avere test unitari è abbastanza utile.

Per SpookyHash, in realtà ho my own C99 version (che risolve anche un off-by-one in V2 dell'implementazione di riferimento). Se stai bene violando la digitazione efficace e la tua architettura supporta l'accesso non allineato (o tutti i dati di input sono allineati), puoi passare il flag del compilatore -DSPOOKY_NOCOPY. Sulla mia macchina x86-64, il guadagno di prestazioni era di circa il 10-20% a seconda delle dimensioni dell'input.

6

In questo momento, compilo questa unità di compilazione con -fno-strict-aliasing, mentre compilo il resto del programma con -fstrict-aliasing.

È possibile fare lo stesso con le ottimizzazioni del tempo di collegamento. Basta non compilare il codice oggetto specifico con ottimizzazioni del tempo di collegamento.

Esempio con clang (stesso con gcc):

clang -flto -O3 -c a.c 
clang -O3 -fno-strict-aliasing b.c  # no -flto and with -fno-strict-aliasing 
clang -flto -O3 -c main.c 
clang a.o b.o main.o -o main