Come impostare condizionalmente l'ottimizzazione del compilatore per le intestazioni dei modelli

Come impostare condizionalmente l'ottimizzazione del compilatore per le intestazioni dei modelli

Fondamentalmente il compilatore deve ridurre al minimo lo spazio senza menzionare che avere lo stesso modello istanziato 2x potrebbe causare problemi se ci fossero membri statici. Quindi, da quello che so, il compilatore sta elaborando il modello per ogni codice sorgente e quindi sceglie una delle implementazioni, oppure posticipa l'effettiva generazione del codice al momento del collegamento. In entrambi i casi è un problema per questa cosa AVX. Ho finito per risolverlo alla vecchia maniera, con alcune definizioni globali che non dipendono da alcun modello o altro. Tuttavia, per applicazioni troppo complesse questo potrebbe essere un grosso problema. Intel Compiler ha un pragma aggiunto di recente (non ricordo il nome esatto), che rende la funzione implementata subito dopo l'utilizzo solo delle istruzioni AVX, il che risolverebbe il problema. Quanto sia affidabile, non lo so.


Ho risolto con successo questo problema forzando tutte le funzioni basate su modelli che verranno utilizzate con diverse opzioni del compilatore in diversi file sorgente per essere inline. Il semplice utilizzo della parola chiave inline di solito non è sufficiente, poiché il compilatore a volte la ignorerà per funzioni più grandi di una certa soglia, quindi devi forzare il compilatore a farlo.

In MSVC++:

template<typename T>
__forceinline int RtDouble(T number) {...}

GCC:

template<typename T>
inline __attribute__((always_inline)) int RtDouble(T number) {...}

Tieni presente che potresti dover forzare in linea qualsiasi altra funzione che RtDouble potrebbe chiamare all'interno dello stesso modulo per mantenere i flag del compilatore coerenti anche in quelle funzioni. Tieni inoltre presente che MSVC++ ignora semplicemente __forceinline quando le ottimizzazioni sono disabilitate, ad esempio nelle build di debug, e in quei casi questo trucco non funzionerà, quindi aspettati un comportamento diverso nelle build non ottimizzate. In ogni caso, può rendere le cose problematiche per il debug, ma in effetti funziona fintanto che il compilatore consente l'inlining.


Penso che la soluzione più semplice sia far sapere al compilatore che quelle funzioni sono effettivamente pensate per essere diverse, usando un parametro template che non fa altro che distinguerle:

File double.h :

template<bool avx, typename T>
int RtDouble(T number)
{
    // Side effect: generates avx instructions
    const int N = 1000;
    float a[N], b[N];
    for (int n = 0; n < N; ++n)
    {
        a[n] = b[n] * b[n] * b[n];
    }    
    return number * 2;
}

File fn_normal.cpp :

#include "fn_normal.h"
#include "double.h"

int FnNormal(int num)
{
    return RtDouble<false>(num);
}

File fn_avx.cpp :

#include "fn_avx.h"
#include "double.h"

int FnAVX(int num)
{
    return RtDouble<true>(num);
}