Sé que el polimorfismo puede agregar una carga perceptible. Llamar a una función virtual es más lento que llamar a una función no virtual. (Toda mi experiencia es sobre GCC, pero creo/escuché que esto es cierto para cualquier compilador real).C++: sobrecarga de polimorfismo de lucha
Muchas veces se llama a una función virtual dada en el mismo objeto una y otra vez; Yo sé que tipo de objeto no cambia, y la mayoría de veces el compilador podría fácilmente deducir que tiene así:
BaseType &obj = ...;
while(looping)
obj.f(); // BaseType::f is virtual
Para acelerar el código que pude volver a escribir el código anterior como esto:
BaseType &obj = ...;
FinalType &fo = dynamic_cast< FinalType& >(obj);
while(looping)
fo.f(); // FinalType::f is not virtual
Me pregunto cuál es la mejor manera de evitar esta sobrecarga debido al polimorfismo en estos casos.
La idea del colado superior (como se muestra en el segundo fragmento) no me parece tan buena: BaseType
podría ser heredada por muchas clases, y tratar de lanzar hacia arriba a todas ellas sería bastante prolijo.
Otra idea podría ser la de almacenar obj.f
en un puntero de función (no lo prueba, no estoy seguro de que mate la sobrecarga en tiempo de ejecución), pero de nuevo este método no se ve perfecto: como el método anterior, requeriría escribir más código y no sería capaz de explotar algunas optimizaciones (p. ej .: si FinalType::f
fuera una función en línea, no se insertaría en línea, pero supongo que la única forma de evitar esto sería con el casting superior obj
a su tipo final ...)
Entonces, ¿hay algún método mejor?
Edit: Bueno, por supuesto esto no va a afectar tanto. Esta pregunta era sobre todo para saber si había algo que hacer, ya que parece que esta sobrecarga se da de forma gratuita (esta sobrecarga parece ser muy fácil de matar) No veo por qué no hacerlo.
Una palabra clave fácil para pequeñas optimizaciones, como C99 restrict
, decirle al compilador que un objeto polimórfico es de tipo fijo es lo que estaba esperando.
De todos modos, solo para responder a los comentarios, un poco de sobrecarga está presente. Miro esta ad-hoc extrema código:
struct Base { virtual void f(){} };
struct Final : public Base { void f(){} };
int main() {
Final final;
Final &f = final;
Base &b = f;
for(int i = 0; i < 1024*1024*1024; ++ i)
#ifdef BASE
b.f();
#else
f.f();
#endif
return 0;
}
Compilación y ejecución que, teniendo tiempos:
$ for OPT in {"",-O0,-O1,-O2,-O3,-Os}; do
for DEF in {BASE,FINAL}; do
g++ $OPT -D$DEF -o virt virt.cpp &&
TIME="$DEF $OPT: %U" time ./virt;
done;
done
BASE : 5.19
FINAL : 4.21
BASE -O0: 5.22
FINAL -O0: 4.19
BASE -O1: 3.55
FINAL -O1: 1.53
BASE -O2: 3.61
FINAL -O2: 0.00
BASE -O3: 3.58
FINAL -O3: 0.00
BASE -Os: 6.14
FINAL -Os: 0.00
supongo única O2, O3 y -Os se Inlining Final::f
.
Y estas pruebas se han ejecutado en mi máquina, ejecutando la última GCC y una CPU AMD Athlon (tm) 64 X2 Dual Core Processor 4000+. Supongo que podría ser mucho más lento en una plataforma más barata.
Por lo tanto, supongo que su código es lento, y lo perfila y encontró que el problema está en el polimorfismo. – wilhelmtell
Si 'f' es virtual en' BaseType' y 'FinalType' se deriva de' BaseType', entonces 'f' también es virtual en' FinalType'. –
También. 'dynamic_cast <>()' tiene un costo de verificación en tiempo de ejecución, y el costo del polimorfismo es una referencia de puntero único. Sugiero que cada vez que diga la palabra "sobrecarga", asegúrese de decir ** exactamente ** qué significa esa sobrecarga, al menos la primera vez que habla de esa sobrecarga. Solo para que quede claro lo que estamos tratando de eliminar aquí. Y entonces, ahora, supongo que perfilaste los dos enfoques y encontraste que el polimorfismo es más lento que tu truco. – wilhelmtell