Come posso forzare il costruttore di copia generato dal compilatore di una classe per * non * essere inline dal compilatore?

StackOverflow https://stackoverflow.com/questions/3870538

Domanda

Alternate titolo della domanda sarebbe: Come avere esplicitamente al compilatore di generare codice per i costruttori generati dal compilatore in un'unità di traduzione specifica?

Il problema che abbiamo di fronte è che per un percorso di codice risultante - accuratamente misurato - prestazione è migliore (di circa il 5%), se le chiamate copia ctor di un oggetto sono non inline, che è se questo costruttore è implementato manualmente. (Abbiamo notato questo perché durante il codice-cleanup il superfluo ctor copia esplicitamente implementato di questa classe (17 membri) è stato rimosso.)

Modifica Nota che sono controllato l'assemblaggio codice generato e hanno fatto in modo che la messa in linea e il codice di generazione sta accadendo come ho descritto per le due versioni di codice differenti.

Si faccia ora la scelta del proprio far cadere il manuale di codice di copia-ctor indietro nel (lo fa esattamente come il compilatore ha generato uno) o la ricerca di qualsiasi altro mezzo di non inlining ctor copia questa classe.

C'è qualche mezzo (per Microsoft Visual C ++) per istanziare esplicitamente le funzioni di classe del compilatore generato in una specifica unità di traduzione o saranno sempre essere inline in ogni unità di traduzione dove sono utilizzati? (Commenti di GCC o altri compilatori sono inoltre invitati a ottenere un quadro migliore della situazione.)


Dal momento che le prime 2 risposte mostrano qualche incomprensione: il compilatore ha generato funzioni di classe sono generati solo dal compilatore stesso se sono non dichiarate o non definite dall'utente. Pertanto modificatori di sorta possono essere applicate a loro, dal momento che queste funzioni non esistono nel codice sorgente.

struct A {
  std::string member;
};

A ha un ctor di default e la copia, un dtor e un operatore di copia. Nessuno di queste funzioni possono essere modificate tramite alcuni declspec perché non esistono nel codice.

struct B {
  std::string member;
  B(B const& rhs);
};

B ora ha un fornito dall'utente copia ctor e l'utente deve per la sua attuazione. Il compilatore non genera codice per esso.


Alcuni più di fondo per i dubbiosi :-) ...

Questo codice viene compilato utilizzando MS Visual C ++, ma è legato a un (like) (in tempo reale) di sistemi embedded. La performance è stata misurata prendendo temporizzazioni su questo sistema e quindi io credo che i ragazzi che hanno preso i tempi avranno alcuni numeri decenti.

La prova è stata eseguita confrontando due versioni del codice allorché il solo differenza è stata la linea contro la copia ctor non-linea di questa classe uno. Tempi con il codice inline erano peggiori di circa il 5%.


ulteriore controllo ha rivelato che ero sbagliato in un punto: Il compilatore generare funzioni separate per costruttori di copia complesse. Lo farà a propria discrezione e dipende anche dalle impostazioni di ottimizzazione. Quindi, nel nostro caso il compilatore sta facendo la cosa sbagliata nelle nostre circostanze specifiche. Dalle risposte finora non sembra siamo in grado di dire al compilatore altrimenti. : - (

È stato utile?

Soluzione 6

Per aggiungere la mia conclusione e per rispondere alla domanda esatta senza entrare nei dettagli:

  1. non possono forza il compilatore, in particolare VC ++, per linea o non in linea un ctor generato dal compilatore / dtor / etc. - ma

  2. L'ottimizzatore sceglierà - al suo potere discrezionale - se il codice inline per una funzione generato compilatore (ctor) o se genera una funzione "reale" per questo codice. Per quanto ne sappia non c'è alcun modo di influenzare la decisione del ottimizzatore a questo riguardo.

Altri suggerimenti

  

$ 12.1 / 5- "Un implicitamente dichiarata   costruttore di default è un inline   membro pubblico della sua classe "..

Quindi, non c'è molto che possiamo fare. Il costruttore implcit deve essere un inline. Qualsiasi altro comportamento in questa materia sarebbe probabilmente un'estensione

Detto questo,

E 'probabile che il costruttore di copia manuale (che è stato rimosso durante la pulizia del codice) stava facendo la cosa giusta. Per fare un esempio, se uno dei membri (su 17) nella classe è un membro di puntatore, è probabile che il costruttore di copia manuale si prese cura di copia profonda (e quindi ha preso un colpo di prestazioni).

Quindi, a meno che non si esamina con attenzione il vostro costruttore di copia manuale, non pensate nemmeno di rimuoverlo e basandosi sul (potenzialmente buggy) costruttore di copia implicita (nel contesto)

dubito fortemente inlining ha nulla a che fare con esso. Se il compilatore Inlines ctor copia generato dal compilatore, perché non sarebbe in linea anche quello esplicitamente definito? (E 'anche insolito che euristiche di ottimizzazione del compilatore non riescono così male da rendere il codice inline 5% più lento)

Prima di saltare alle conclusioni,

  • Controllare il generato assemblea per verificare che le due versioni effettivamente fare la stessa cosa (e nello stesso ordine, utilizzano lo stesso montaggio e così via, altrimenti che potrebbe essere la fonte della vostra differenza di prestazioni)
  • controllo che il compilatore generato quella effettivamente è essendo inlined, e quella definita manualmente è non .

Se questo è il caso, potrebbe aggiornare la tua domanda con queste informazioni?

Non c'è modo in C ++ per indicare se una funzione generato dal compilatore dovrebbe o non dovrebbe essere inline. Nemmeno estensioni vendor specifici come __declspec(noinline) vi aiuterà lì, dal momento che sei esplicitamente consegna ogni responsabilità per la funzione al compilatore. Così i sceglie compilatore che cosa fare con esso, come implementarlo e se non inline esso. È possibile non entrambi dire "per favore implementare questa funzione per me", e allo stesso tempo "per favore fatemelo controllare come la funzione viene implementata". Se si desidera controllare sulla funzione, è necessario per la sua attuazione. ;)

In C ++ 0x, potrebbe essere possibile (a seconda di come queste estensioni vendor-specifici interagiscono con funzioni dichiarate = default).

Ma ancora una volta, non sono convinto che inlining è il problema. Molto probabilmente, le due funzioni solo risultato in codice assembly diverso generato.

__declspec (noinline) .

La documentazione dice che essa si applica solo alle funzioni membro, ma in realtà funziona con le funzioni gratuita.

è spesso meglio isolarlo per alcuni tipi di base che si sa sono problematici. Esempio A:

class t_std_string {
    std::string d_string;
public:
    /* ... */

    /* defined explicitly, and out of line -- you know what to do here */
    t_std_string();
    t_std_string(const std::string& other);
    t_std_string(const t_std_string& other);
    ~t_std_string();

    inline std::string& get() { return this->d_string; }
    inline const std::string& get() const { return this->d_string; }
    /* ... */
};

struct B {
    t_std_string member;
    /* 16 more */
    /* ... */
};

o si può prendere alcuni dei gratuitamente. Esempio B:

/* B.hpp */

struct B {
private:

    /* class types */
    struct t_data {
        std::string member;

        /* 16 more ... */
    public:
        /* declare + implement the ctor B needs */

        /* since it is otherwise inaccessible, it will only hurt build times to make default ctor/dtor implicit (or by implementing them in the header, of course), so define these explicitly in the cpp file */
        t_data();
        ~t_data();

        /* allow implicit copy ctor and assign -- this could hurt your build times, however. it depends on the complexity/visibility of the implementation of the data and the number of TUs in which this interface is visible. since only one object needs this... it's wasteful in large systems */
    };
private:

    /* class data */
    t_data d_data;
public:
    /* you'll often want the next 4 out of line
       -- it depends on how this is created/copied/destroyed in the wild
     */
    B();
    B(const B& other);
    ~B();
    B& operator=(const B&);
};

/* B.cpp */

/* assuming these have been implemented properly for t_data */
B::B() : d_data() {
}

B::B(const B& other) : d_data(other) {
}

B::~B() {
}

B& B::operator=(const B&) {
    /* assuming the default behaviour is correct...*/
    this->d_data = other.d_data;
    return *this;
}
/* continue to B::t_data definitions */

Si potrebbe utilizzare una sorta di oggetto nidificato. In questo modo il costruttore copia dell'oggetto nidificato può essere lasciato come predefinito esente da manutenzione, ma avete ancora un costruttore di copia esplicitamente creato che è possibile dichiarare noinline.

class some_object_wrapper {
    original_object obj;
    __declspec(noinline) some_object_wrapper(const some_object_wrapper& ref) 
        : obj(ref) {}
    // Other function accesses and such here
};

Se siete disperati, si potrebbe compilare la classe in questione separatamente in una Lib e link ad esso. Cambiandolo in un'unità diversa traduzione non fermerà VC ++ da inlining esso. Inoltre, devo domanda se stanno in realtà facendo la stessa cosa. Perché si sceglie di implementare un costruttore di copia manuale se lo fa lo stesso come il costruttore di copia di default?

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