¿Tener varios niveles de clases base ralentiza una clase? A deriva B deriva C deriva D deriva F deriva G, ...¿Varios niveles de clases base ralentizan una clase/estructura en C++?
¿La herencia múltiple ralentiza una clase?
¿Tener varios niveles de clases base ralentiza una clase? A deriva B deriva C deriva D deriva F deriva G, ...¿Varios niveles de clases base ralentizan una clase/estructura en C++?
¿La herencia múltiple ralentiza una clase?
Las llamadas a funciones no virtuales no tienen absolutamente ningún rendimiento en el tiempo de ejecución, de acuerdo con el mantra de C++ que no debe pagar por lo que no usa. En una llamada a función virtual, generalmente paga por una búsqueda de puntero adicional, sin importar cuántos niveles de herencia o cantidad de clases base tenga. Por supuesto, esto es toda la implementación definida.
Editar: Como se señaló en otra parte, en algunos escenarios de herencia múltiple, se requiere un ajuste al puntero 'this' antes de realizar la llamada. Raymond Chen describe how this works para objetos COM. Básicamente, llamar a una función virtual en un objeto que hereda desde múltiples bases puede requerir una resta adicional y una instrucción jmp además de la búsqueda adicional del puntero requerida para una llamada virtual.
Si no hay funciones virtuales, entonces no debería. Si hay, entonces, hay un impacto en el rendimiento al llamar a las funciones virtuales, ya que se llaman a través de punteros de función u otros métodos indirectos (depende de la situación). Sin embargo, no creo que el impacto esté relacionado con la profundidad de la jerarquía de herencia.
Brian, para que quede claro y responda su comentario. Si no hay funciones virtuales en ningún lugar en su árbol de herencia, entonces no hay impacto en el rendimiento.
¿y si no hay funciones virtuales? –
Entonces no importa en absoluto, porque todas las llamadas a funciones se pueden resolver en tiempo de compilación. Es decir. el compilador sabe exactamente a qué función se llamará, lo que ahorra una búsqueda vtable. – Thomas
Sí, si usted está haciendo referencia así:
// F is-a E,
// E is-a D and so on
A* aObject = new F();
aObject->CallAVirtual();
A continuación, se trabaja con un puntero a un objeto Un tipo. Dado que está llamando a una función que es virtual, tiene que buscar la tabla de funciones (vtable) para obtener los punteros correctos. Hay algunos gastos generales para eso, sí.
Llamar a una función virtual es un poco más lento que llamar a una función no virtual. Sin embargo, no creo que importe cuán profundo es el árbol de tu herencia.
Pero esta no es la diferencia de la que normalmente debería preocuparse.
Aunque no estoy del todo seguro, creo que a menos que esté usando métodos virtuales, el compilador debería poder optimizarlo lo suficiente como para que la herencia no importe demasiado.
Sin embargo, si está llamando a funciones en la clase base que llaman a funciones en su clase base, y así sucesivamente, podría afectar el rendimiento.
En esencia, depende en gran medida de cómo haya estructurado su árbol de herencia.
[Jerarquías de herencia profunda] aumenta en gran medida la carga de mantenimiento agregando complejidad innecesaria, obligando a los usuarios a aprender las interfaces de muchas clases, incluso cuando lo único que quieren hacer es utilizar una clase derivada específica. También puede tener un impacto en el uso de la memoria y el rendimiento del programa agregando tablas virtuales e indirectas innecesarias a las clases que realmente no las necesitan. Si se encuentra creando con frecuencia jerarquías de herencia profunda, debe revisar su estilo de diseño para ver si ha recogido este mal hábito. Las jerarquías profundas rara vez se necesitan y casi nunca son buenas. Y si no cree eso pero piensa que "OO simplemente no es OO sin mucha herencia", entonces un buen contraejemplo a considerar es la biblioteca estándar [C++] en sí misma.- Herb Sutter
gracias, pero esto no responde a la pregunta. –
No hay diferencia de velocidad entre las llamadas virtuales en diferentes niveles ya que todas se aplanan en el vtable (apuntando a las versiones más derivadas de los métodos reemplazados). Así, llamando al ((A *) inst) - > Método() cuando inst es una instancia de B es la sobrecarga mismo como cuando inst es una instancia de D .
Ahora, una llamada virtual es más costosa que una llamada no virtual, pero esto se debe a la desreferencia del puntero y no a la función de qué tan profunda es realmente la jerarquía de clases.
Como se ha mencionado varias veces, una jerarquía de herencia única profundamente anidada debe imponer ninguna sobrecarga adicional para una llamada virtual (por encima de la sobrecarga impuesta para cualquier llamada virtual).
Sin embargo, cuando se trata de herencia múltiple, a veces hay una sobrecarga adicional muy pequeña al llamar a la función virtual a través de un puntero de clase base. En este caso, algunas implementaciones tienen la función virtual pasar por un pequeño golpe seco que ajusta el puntero 'this' desde
(static_cast<Base*>(this) == this)
no es necesariamente cierto dependiendo de la disposición objeto.
Tenga en cuenta que todo esto es muy , muy dependiente de la implementación.
Ver Lippman de "dentro del C++ Object Model" Capítulo 4.2 - Funciones miembro virtual/Funciones virtuales bajo MI
llamadasvirtuales en sí son más tiempo que las llamadas normales porque tiene que buscar la dirección de la función real de llamar desde el vtable
Además, las optimizaciones del compilador como la alineación pueden ser difíciles de realizar debido al requisito de búsqueda. Situaciones en las que la expansión en línea no es posible en sí puede dar lugar a una muy alta carga debida a la pila de operaciones del pop y el empuje y saltar
es un estudio adecuado, que dice que la sobrecarga puede ser tan alta como 50% http://www.cs.ucsb.edu/~urs/oocsb/papers/oopsla96.pdf
Aquí está Otro recurso que analiza un efecto secundario de tener una gran biblioteca de clases virtuales http://keycorner.org/pub/text/doc/kde-slow.txt
El envío de llamadas virtuales con múltiples herencias es específico del compilador, por lo que la implementación también tendrá un efecto en este caso.
En cuanto a su pregunta específica de tener un gran número de clases base, generalmente el diseño de la memoria de un objeto de clase tendría los ptr vtbl para todas las otras clases constituyentes dentro de él.
Marque esta página para un diseño de muestra de vtable - http://www.codesourcery.com/public/cxx-abi/cxx-vtable-ex.html
Así que una llamada a un método implementado por una clase más profundamente en el heierarchy habría todavía sólo ser una sola vía indirecta y no múltiples indirecciones como pareces pensar. La llamada no tiene que navegar de clase a clase para finalmente encontrar la función exacta para llamar.
Sin embargo, si usa composición en lugar de herencia cada llamada de puntero sería una llamada virtual y esa sobrecarga estaría presente y si dentro de esa llamada virtual si esa clase usa más composición, se realizarían más llamadas virtuales. Ese tipo de diseño sería más lento dependiendo de la cantidad de llamadas que hizo.
Como se señala en Corey Ross, el vtable se conoce en tiempo de compilación para cualquier clase derivada de hoja, por lo que el costo de la llamada virtual debería ser el mismo independientemente de la estructura de la jerarquía.
Esto, sin embargo, no se puede decir de dynamic_cast
. Si considera cómo puede implementar dynamic_cast
, ¡un enfoque básico tendrá una búsqueda O (n) a través de su jerarquía!
En el caso de una jerarquía de herencia múltiple, también están pagando un pequeño costo para convertir entre diferentes clases en la jerarquía:
sturct A { int i; };
struct B { int j; };
struct C : public A, public B { int k ; };
// Let's assume that the layout of C is: { [ int i ] [ int j ] [int k ] }
void foo (C * c) {
A * a = c; // Probably has zero cost
B * b = c; // Compiler needed to add sizeof(A) to 'c'
c = static_cast<B*> (b); // Compiler needed to take sizeof(A)' from 'b'
}
Casi todas las respuestas apuntan hacia si o no los métodos virtuales serían más lento en el ejemplo del PO, pero creo que el OP simplemente pregunta si tener varios niveles de herencia en sí es lento. La respuesta es, por supuesto, no, ya que todo esto sucede en tiempo de compilación en C++. Sospecho que la pregunta se basa en la experiencia con los lenguajes de script, donde dichos gráficos de herencia pueden ser dinámicos, y en ese caso, podría ser más lento.
Tener constructores no triviales en un árbol de herencia profunda puede ralentizar la creación de objetos, cuando cada creación de un niño genera llamadas de función a todos los constructores padres hasta la base.
Cuando se usa herencia múltiple, necesita un desplazamiento adicional del puntero dependiendo de la función llamada, consulte http://en.wikipedia.org/wiki/Tunk –