2013-03-14 10 views
8

Io lavoro con due computer. Uno senza supporto AVX e uno con AVX. Sarebbe utile che il mio codice trovi il set di istruzioni supportato dalla mia CPU in fase di esecuzione e scelga il percorso del codice appropriato. Ho seguito i suggerimenti di Agner Fog per creare un dispatcher della CPU (http://www.agner.org/optimize/#vectorclass). Tuttavia, sul mio maching senza la compilazione AVX e il collegamento con Visual Studio, il codice con AVX attivato causa il crash del codice durante l'esecuzione.Cpu dispatcher per Visual Studio per AVX e SSE

Voglio dire ad esempio che ho due file sorgente uno con il set di istruzioni SSE2 definito con alcune istruzioni SSE2 e un altro con il set di istruzioni AVX definito e con alcune istruzioni AVX. Nella mia funzione principale, se faccio riferimento solo alle funzioni SSE2, il codice si blocca ancora in virtù dell'utilizzo di qualsiasi codice sorgente con AVX abilitato e con le istruzioni AVX. Qualche indizio su come posso risolvere questo problema?

Modifica: Ok, penso di aver isolato il problema. Sto utilizzando classe Vector di Agner Fog e ho definito tre file di origine come:

//file sse2.cpp - compiled with /arch:SSE2 
#include "vectorclass.h" 
float func_sse2(const float* a) { 
    Vec8f v1 = Vec8f().load(a); 
    float sum = horizontal_add(v1); 
    return sum; 
} 
//file avx.cpp - compiled with /arch:AVX 
#include "vectorclass.h" 
float func_avx(const float* a) { 
    Vec8f v1 = Vec8f().load(a); 
    float sum = horizontal_add(v1); 
    return sum; 
} 
//file foo.cpp - compiled with /arch:SSE2 
#include <stdio.h> 
extern float func_sse2(const float* a); 
extern float func_avx(const float* a); 
int main() { 
    float (*fp)(const float*a); 
    float a[] = {1,2,3,4,5,6,7,8}; 
    int iset = 6; 
    if(iset>=7) { 
     fp = func_avx; 
    } 
    else { 
     fp = func_sse2; 
    } 
    float sum = (*fp)(a); 
    printf("sum %f\n", sum); 
} 

Questo si blocca. Se invece utilizzo Vec4f in func_SSE2 non si blocca. Non lo capisco Posso usare Vec8f con SSE2 da solo fino a quando non ho un altro file sorgente con AVX. manuale dell'utilizzatore Agner nebbia dice

"Non v'è alcun vantaggio in base alle classi 256 bit virgola mobile vettoriali (Vec8f, Vec4d) a meno che sia specificato il set di istruzioni AVX, ma può essere conveniente utilizzare queste classi comunque se la lo stesso codice sorgente viene utilizzato con e senza AVX. Ogni vettore a 256 bit verrà semplicemente diviso in due vettori a 128 bit durante la compilazione di senza AVX. "

Tuttavia, quando ho due file sorgente con Vec8f uno compilato con SSE2 e uno compilato con AVX, si verifica un arresto anomalo.

Edit2: posso farlo funzionare da linea di comando

>cl -c sse2.cpp 
>cl -c /arch:AVX avx.cpp 
>cl foo.cpp sse2.obj avx.obj 
>foo.exe 

Edit3: Questo, tuttavia, si blocca

>cl -c sse2.cpp 
>cl -c /arch:AVX avx.cpp 
>cl foo.cpp avx.obj sse2.obj 
>foo.exe 

Un altro indizio. Apparentemente, l'ordine di collegamento conta. Crolla se avx.obj è prima di sse2.obj ma se sse2.obj è prima di avx.obj non si blocca. Non sono sicuro che scelga il percorso corretto del codice (non ho accesso al mio sistema AVX in questo momento) ma almeno non si blocca.

+0

Quali sono i dettagli dello schianto? Hai identificato l'istruzione fallita in un debugger? –

+0

Bene il debugger mostra che func_SSE sta provando ad usare le istruzioni AVX. Non so perché. Ma sono riuscito a far funzionare il codice senza crash utilizzando la riga di comando. Ho aggiunto i comandi sopra. Ancora non so come farlo con l'IDE. Il lato positivo è stato compilato dalla riga di comando per la prima volta in Windows! È l'unico modo che compilo su Linux. –

+0

Hai utilizzato la generazione del codice link-time dall'IDE? –

risposta

1

Inserire le funzioni SSE e AVX in diversi file CPP e assicurarsi di compilare la versione SSE senza /arch:AVX.

+0

Questo è esattamente quello che ho fatto. –

+0

Questo basta eseguirlo in debugger. Quando la CPU genererà un'eccezione "istruzione non valida", vedrai l'origine di questa istruzione. È probabile che la tua CPU non AVX non supporti alcune istruzioni SSE che usi. Ci sono molte generazioni di istruzioni SSE: SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, e SSE4A (include SSE3, ma non SSSE3, SSE4.1 o SSE4.2). –

+0

La mia CPU supporta fino a SSE4.2. L'ho controllato con CPU-Z.Ma ora sto provando una versione ridotta del codice senza la vector class e funziona. Dovrò tornare da te ... –

2

Il fatto che l'ordine di collegamento contenga mi fa pensare che potrebbe esserci qualche tipo di codice di inizializzazione nel file obj. Se il codice di inizializzazione è comune, viene preso solo il primo. Non riesco a riprodurlo, ma dovresti essere in grado di vederlo in un elenco di assembly (compile con/c /Ftestavx.asm)

+0

Il dispatcher non si arresta in modo anomalo sul mio sistema con AVX ma si blocca sul mio sistema senza. Puoi testarlo su un sistema senza AVX? Forse il sistema su AVX non sta scegliendo le istruzioni SSE, ma visto che ha AVX funziona ancora? L'elenco di assembly è un po 'troppo avanzato per me in questo momento, quindi è probabile che dovrò tornare a questo. –

7

Mi rendo conto che questa è una vecchia domanda e che la persona che lo ha chiesto sembra non essere più in giro, ma ho avuto lo stesso problema ieri. Ecco cosa ho elaborato.

Quando si compila, entrambi i file sse2.cpp e avx.cpp producono file oggetto che non contengono solo la funzione ma anche qualsiasi funzione di modello richiesta. (ad es.Vec8f::load) Queste funzioni del modello vengono anche compilate utilizzando il set di istruzioni richiesto.

Ciò significa che entrambi i file oggetto sse2.obj e avx.obj conterranno entrambe le definizioni di Vec8f::load ciascuna compilata utilizzando i rispettivi set di istruzioni.

Tuttavia, poiché il compilatore considera Vec8f::load come visibile esternamente, inserisce una sezione "COMDAT" del file oggetto con un'etichetta "selectany" (aka "scegli qualsiasi"). Questo dice al linker che se vede più definizioni di questo simbolo, ad esempio in 2 file oggetto diversi, allora è permesso di sceglierne uno a piacere. (Lo fa per ridurre il codice duplicato nell'eseguibile finale che altrimenti sarebbe gonfiato di dimensioni da più definizioni di template e funzioni inline.)

Il problema che si sta avendo è direttamente correlato a questo in quanto l'ordine dell'oggetto i file passati al linker influiscono su quale seleziona. Nello specifico, sembra che stia selezionando la prima definizione che vede.

Se questo era avx.obj, verrà sempre utilizzata la versione compilata AVX di Vec8F::load. Questo si bloccherà su una macchina che non supporta quel set di istruzioni. D'altra parte se sse2.obj è il primo, allora verrà sempre utilizzata la versione compilata SSE2. Questo non si blocca ma utilizzerà le istruzioni SSE2 anche se AVX è supportato. (. Prodotta utilizzando l'opzione/mappa)

Che questo sia il caso può essere visto se si guarda alla linker 'mappa' output di file Ecco le pertinenti (a cura) estratti -

// 
// link with sse2.obj before avx.obj 
// 
0001:00000080 _main        foo.obj 
0001:00000330 [email protected]@[email protected]    sse2.obj 
0001:00000420 [email protected]@[email protected]    sse2.obj 
0001:00000440 [email protected]@[email protected][email protected]@@Z  sse2.obj 
0001:00000470 [email protected]@[email protected]     sse2.obj <-- sse2 version used 
0001:00000490 [email protected]@[email protected]@XZ  sse2.obj 
0001:000004c0 [email protected]@@[email protected]@XZ sse2.obj 
0001:000004f0 [email protected]@@[email protected]@XZ sse2.obj 
0001:00000520 [email protected]@@[email protected]@Z  sse2.obj <-- sse2 version used 
0001:00000680 [email protected]@[email protected]    avx.obj 
0001:00000740 [email protected]@[email protected]@XZ  avx.obj 

// 
// link with avx.obj before sse2.obj 
// 
0001:00000080 _main        foo.obj 
0001:00000270 [email protected]@[email protected]    avx.obj 
0001:00000330 [email protected]@[email protected]     avx.obj <-- avx version used 
0001:00000350 [email protected]@[email protected]@XZ  avx.obj 
0001:00000380 [email protected]@@[email protected]@Z  avx.obj <-- avx version used 
0001:00000580 [email protected]@[email protected]    sse2.obj 
0001:00000670 [email protected]@[email protected]    sse2.obj 
0001:00000690 [email protected]@[email protected][email protected]@@Z  sse2.obj 
0001:000006c0 [email protected]@[email protected]@XZ  sse2.obj 
0001:000006f0 [email protected]@@[email protected]@XZ sse2.obj 
0001:00000720 [email protected]@@[email protected]@XZ sse2.obj 

Per quanto riguarda aggiustandolo, questa è un'altra questione. In questo caso, il seguente smembramento dovrebbe funzionare forzando la versione avx ad avere le sue versioni con nomi diversi delle funzioni del template. Ciò aumenterà la dimensione eseguibile risultante in quanto conterrà più versioni della stessa funzione anche se le versioni sse2 e avx sono identiche.

// avx.cpp 
namespace AVXWrapper { 
\#include "vectorclass.h" 
} 
using namespace AVXWrapper; 

float func_avx(const float* a) 
{ 
    ... 
} 

Ci sono alcune limitazioni importanti anche se - (a) se il file incluso gestisce qualsiasi forma di stato globale che sarà più veramente globale come avrete 2 versioni 'semi-globali', e (b) non sarai in grado di passare le variabili vettoriali come parametri tra altro codice e funzioni definite in avx.cpp.

+0

Ho fatto questa domanda. Sono riuscito a farlo funzionare. Non so quale fosse il problema. Ma ora funziona bene per me. Provalo con cmake https://stackoverflow.com/questions/23808094/create-separate-object-files-from-the-same-source-code-and-link-to-an-executable –

Problemi correlati