¿Cómo puedo forzar el generado por el compilador copia constructor de una clase que * no * ser inline por el compilador?

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

Pregunta

título de la pregunta alternativo sería la siguiente: ¿Cómo tener explícitamente el compilador genera código para los constructores generados por el compilador en una unidad de traducción específica?

La cara problema que es que por una ruta de código que resulta de la - a fondo medido - el rendimiento es mejor (en aproximadamente un 5%) si las llamadas copia-ctor de un objeto son no entre líneas, es decir, si este constructor se lleva a cabo de forma manual. (Nos dimos cuenta de esto porque durante el código de limpieza lo superfluo ctor copia implementado explícitamente de esta clase (17 miembros) se retiró.)

Editar Nota que Tienes comprobado el código ensamblador generado y han asegurado de que la generación de procesos en línea y el código está sucediendo como lo describo para las dos versiones de código diferentes.

Nos enfrentamos ahora la opción de simplemente dejar caer la parte posterior de código copia-ctor manual (que hace exactamente lo mismo que el compilador genera uno) o la búsqueda de cualquier otro medio de no inlining la ctor copia de esta clase.

¿Hay cualquier medio (para Microsoft Visual C ++) para crear una instancia de forma explícita el compilador genera funciones de clase en una unidad de traducción específica o van a ser siempre entre líneas en cada unidad de traducción en el que se utilizan? (Comentarios sobre gcc u otros compiladores también están invitados a tener una mejor idea de la situación.)


Desde las 2 primeras respuestas muestran un malentendido: El compilador genera funciones de clase única se genera por el propio compilador si están no declaradas o no definidos por el usuario. Por lo tanto no hay ningún tipo modificadores se pueden aplicar a ellos, ya que estas funciones no existen en el código fuente.

struct A {
  std::string member;
};

A tiene un defecto y Héctor copia, una dtor y un operador de copia. Ninguna de estas funciones pueden ser modificados a través de algún declspec debido a que no existen en el código.

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

B ahora tiene un suministrada por el usuario copia ctor y el usuario tiene que ponerlo en práctica. El compilador no generará código para ello.


Algunos mayores antecedentes para los que dudan :-) ...

Este código se compila utilizando MS Visual C ++, pero está vinculado para una (en tiempo real) sistema embebido (de tipo). El rendimiento se mide tomando los tiempos en este sistema y por lo tanto creo que los chicos que tomaron los tiempos tendrán algunos números decentes.

La prueba se llevó a cabo mediante la comparación de dos versiones de código en el que el solamente diferencia fue la línea del frente Héctor copia no-inline de esta clase uno. Sincronizaciones con el código inline fueron peores en aproximadamente un 5%.


Además comprobación ha revelado que estaba equivocado en un punto: El compilador generar funciones separadas para los constructores de copia complejos. Esto se hará en su propia discreción y también depende de los ajustes de optimización. Así, en nuestro caso el compilador está haciendo las cosas mal en nuestras circunstancias específicas. A partir de las respuestas hasta el momento no parece que podemos decir que el compilador lo contrario. : - (

¿Fue útil?

Solución 6

Para añadir mi propia conclusión y para responder a la pregunta exacta, sin entrar en detalles:

  1. no pueden fuerza el compilador, específicamente VC ++, para en línea o no en línea una ctor generado por el compilador / dtor / etc. - y

  2. El optimizador elegirá - en su discreción - si inlines el código para una función generado por el compilador (Héctor) o si se genera una función "real" para este código. Que yo sepa no hay manera de influir en la decisión del optimizador en este sentido.

Otros consejos

  

$ 12.1 / 5- "Un implícitamente declarado   constructor por defecto es una línea   miembro público de su clase "..

Así que no hay mucho que podamos hacer. El constructor implcit tiene que ser una línea. Cualquier otro comportamiento en este respecto sería probablemente una extensión

Una vez dicho esto,

Es probable que el constructor de copia manual (que se eliminan durante la limpieza del código) estaba haciendo lo correcto. A modo de ejemplo, si uno de los miembros (de 17) en su clase es un miembro de puntero, lo más probable es que el constructor de copia manual de cuidó de copia profunda (y por lo tanto tuvo un impacto en el rendimiento).

Así que, a menos que revise cuidadosamente su constructor de copia manual, ni siquiera pensar en sacarlo y confiando en la (potencialmente con errores) copia implícita constructor (en su contexto)

I altamente duda inlining tiene nada que ver con ello. Si el compilador inlines la ctor copia generado por el compilador, ¿por qué no también en línea el definido explícitamente? (También es inusual que la heurística de optimización del compilador fallan tan mal como para hacer que el código inline 5% más lento)

antes de sacar conclusiones,

  • Comprobar el montaje genera para verificar que las dos versiones realmente hacen exactamente lo mismo (y en el mismo orden, usando la misma estructura y así sucesivamente, ya que de lo contrario que podría ser la fuente de su diferencia de rendimiento)
  • Compruebe que el compilador genera una realidad es está entre líneas, y la definida manualmente es no .

Si ese es el caso, ¿podría actualizar su pregunta con esta información?

No hay forma en C ++ para indicar si una función generado por el compilador debe o no debe ser inline. Ni siquiera las extensiones de proveedores específicos, tales como __declspec(noinline) le ayudarán allí, ya que está explícitamente la entrega de toda responsabilidad por la función para el compilador. Así que escoge el compilador qué hacer con él, cómo implementarlo y si es o no es inline. No se puede tanto decir "por favor implementar esta función para mí", y al mismo tiempo "por favor hágamelo controlar cómo se implementa la función". Si desea controlar la función, hay que ponerla en práctica. ;)

En C ++ 0x, it puede ser posible (dependiendo de cómo estas extensiones de proveedores específicos interactuar con funciones declarados como = default).

Pero, de nuevo, no estoy convencido de que los procesos en línea es la cuestión. Lo más probable, las dos funciones simplemente como resultado diferentes código de montaje está generando.

__declspec (noinline) .

La documentación dice que sólo se aplica a las funciones miembro, pero en realidad se trabaja con funciones libres también.

a menudo es mejor para aislarlo a unos pocos tipos básicos que se sabe que son problemáticos. Ejemplo 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 puede tomar algo de él de forma gratuita. Ejemplo 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 */

Se podría utilizar algún tipo de objeto anidado. De este modo copia constructor del objeto anidado se puede dejar como el valor por defecto sin necesidad de mantenimiento, pero todavía tiene un constructor de copia creada explícitamente que se puede declarar 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
};

Si estás desesperado, se podría compilar la clase en cuestión por separado en un .lib y enlazar con él. Si lo cambia a una unidad de traducción diferente, no dejará de VC ++ de inlining ella. Además, tengo a la pregunta de si en realidad están haciendo lo mismo. ¿Por qué se implementa un constructor de copia manual si se hace lo mismo que el constructor de copia predeterminada?

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top