Domanda

Ho una semplice classe che assomiglia Boost.Array. Ci sono due parametri del modello T e N. Uno svantaggio di Boost.Array è che ogni metodo che usi come un array, deve essere un modello con parametro N (T è OK). La conseguenza è che l'intero programma tende ad essere un modello. Un'idea è quella di creare un'interfaccia (classe astratta con funzioni virtuali solo puri), che dipende solo da T (qualcosa come ArrayInterface). Ora ogni altra classe di accesso solo l'interfaccia, e necessita quindi solo il modello parametro T (che a differenza di N, è più o meno sempre conosciuto). Lo svantaggio qui è l'overhead della chiamata virtuale (più l'occasione mancata per le chiamate in linea), se viene utilizzata l'interfaccia. Fino a qui solo i fatti.

template<typename T>
class ArrayInterface {
public:
    virtual ~ArrayInterface() {};
    virtual T Get(int i) = 0;
};

template<typename T, int N>
class Array : ArrayInterface<T> {
public:
    T Get(int i) { ... }
};

template<typename T, int N>
class ArrayWithoutInterface {
public:
    T Get() { ... }
};

ma il mio vero problema sta da qualche altra parte. Quando mi estendo Boost.Array con un'interfaccia, un'esemplificazione diretta di Boost.Array diventa lento (fattore 4 in un caso, dove è importante). Se rimuovo l'interfaccia, Boost.Array è veloce come prima. Ho capito, se un metodo viene chiamato attraverso ArrayInterface c'è un overhead, che è OK. Ma io non capisco perché l'una chiamata a un metodo diventa più lento se c'è solo un'interfaccia aggiuntiva con metodi virtuali solo puri e la classe si chiama direttamente.

Array<int, 1000> a;
a.Get(0); // Slow

ArrayWithoutInterface<int, 1000> awi;
awi.Get(0); // Fast

GCC 4.4.3 e Clang 1.1 mostrano lo stesso comportamento.

È stato utile?

Soluzione

Due motivi:

  • l'associazione tardiva è lento
  • metodi virtuali non può essere inline

Altri suggerimenti

Questo comportamento è previsto: si sta invocando un metodo virtuale. Sia che l'invoca, direttamente o tramite un puntatore alla classe base non è rilevante in un primo momento:. In entrambi i casi, la chiamata ha avuto modo di passare attraverso la tabella di funzione virtuale

Per una chiamata semplice come Get (che semplicemente dereferenziazioni una cella di matrice, presumibilmente senza controllo dei limiti), questo può effettivamente fare la differenza fattore 4.

Ora, un buon compilatore potrebbe vedere che l'aggiunta indiretto non è necessaria perché il tipo dinamico dell'oggetto (e quindi il bersaglio metodo di chiamata) è noto al momento della compilazione. Sono un po 'sorpreso che GCC a quanto pare non ottimizzare questo (fatto si compila con -O3?). Poi di nuovo, è solo un'ottimizzazione.

mi piacerebbe in disaccordo con la tua conclusione che 'l'intero programma tende ad essere un modello': sembra a me come si sta cercando di risolvere un non-problema.

Tuttavia, non è chiaro cosa si intende per 'estendere Boost.Array con un'interfaccia': stai modificando la fonte di boost::array introducendo l'interfaccia? Se è così, ogni istanza array si sta creando deve trascinare un puntatore vtable lungo, anche se non si utilizzano i metodi virtuali. L'esistenza di metodi virtuali può anche rendere il compilatore diffidenti nell'utilizzo ottimizzazioni aggressive possibili metodi non virtuali in una classe puramente header-definito.

A cura: ... e, naturalmente, è sono con il metodo virtuale. Si impiegano tecniche di analisi codice piuttosto avanzata dal compilatore per essere certi di una chiamata virtuale può essere ottimizzata via.

Se si dispone di un metodo virtuale che non viene mai esteso, potrebbe essere possibile che il compilatore è ottimizzare la parte virtuale dei metodi. Durante una chiamata di metodo non virtuale normale, il flusso del programma andrà direttamente dal chiamante al metodo. Tuttavia, quando il metodo è segnato virtuale, la CPU deve prima passare a una tabella virtuale, quindi trovare il metodo che stai cercando, e poi passare a quel metodo.

Ora, questo di solito non è troppo evidente. Se il metodo che si sta chiamando porta 100ms per l'esecuzione, anche se la vostra tabella di ricerca virtuale richiede 1ms, non è andando alla materia. Ma se, nel caso di un array, il metodo prende 0,5 ms per eseguire quella goccia 1ms prestazioni sta per essere abbastanza evidente.

non c'è molto che si può fare su di esso, ad eccezione non si estendono Boost.Array, o rinominare i metodi in modo che non hanno la precedenza.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top