Bueno, el perfilador nunca miente.
Como tengo una jerarquía bastante estable de 18-20 tipos que no está cambiando mucho, me preguntaba si solo usar un simple miembro enumerado haría el truco y evitaría el supuesto costo "alto" de RTTI. Era escéptico si RTTI era de hecho más costoso que solo la if
declaración que presenta. Chico, oh chico, ¿es así?
Resulta que RTTI es costoso, mucho más costoso que una if
declaración equivalente o una simple switch
en una variable primitiva en C ++. Así que la respuesta de S. Lott no es completamente correcto, no es un costo adicional para el RTTI, y es no debido a simplemente tener una if
declaración en la mezcla. Es debido a que RTTI es muy costoso.
Esta prueba se realizó en el compilador Apple LLVM 5.0, con las optimizaciones de stock activadas (configuración del modo de lanzamiento predeterminado).
Entonces, tengo debajo de 2 funciones, cada una de las cuales descubre el tipo concreto de un objeto a través de 1) RTTI o 2) un interruptor simple. Lo hace 50,000,000 de veces. Sin más preámbulos, les presento los tiempos de ejecución relativos para 50,000,000 de carreras.
Así es, dynamicCasts
tomaron el 94% del tiempo de ejecución. Mientras que el regularSwitch
bloque solo tomó 3.3% .
En pocas palabras: si puede permitirse la energía para conectar y enum
escribir como lo hice a continuación, probablemente lo recomendaría, si necesita hacer RTTI y el rendimiento es primordial. Solo toma configurar el miembro una vez (asegúrese de obtenerlo a través de todos los constructores ), y asegúrese de nunca escribirlo después.
Dicho esto, hacer esto no debería estropear sus prácticas de OOP ... solo debe usarse cuando la información de tipo simplemente no está disponible y se encuentra acorralado en el uso de RTTI.
#include <stdio.h>
#include <vector>
using namespace std;
enum AnimalClassTypeTag
{
TypeAnimal=1,
TypeCat=1<<2,TypeBigCat=1<<3,TypeDog=1<<4
} ;
struct Animal
{
int typeTag ;// really AnimalClassTypeTag, but it will complain at the |= if
// at the |='s if not int
Animal() {
typeTag=TypeAnimal; // start just base Animal.
// subclass ctors will |= in other types
}
virtual ~Animal(){}//make it polymorphic too
} ;
struct Cat : public Animal
{
Cat(){
typeTag|=TypeCat; //bitwise OR in the type
}
} ;
struct BigCat : public Cat
{
BigCat(){
typeTag|=TypeBigCat;
}
} ;
struct Dog : public Animal
{
Dog(){
typeTag|=TypeDog;
}
} ;
typedef unsigned long long ULONGLONG;
void dynamicCasts(vector<Animal*> &zoo, ULONGLONG tests)
{
ULONGLONG animals=0,cats=0,bigcats=0,dogs=0;
for( ULONGLONG i = 0 ; i < tests ; i++ )
{
for( Animal* an : zoo )
{
if( dynamic_cast<Dog*>( an ) )
dogs++;
else if( dynamic_cast<BigCat*>( an ) )
bigcats++;
else if( dynamic_cast<Cat*>( an ) )
cats++;
else //if( dynamic_cast<Animal*>( an ) )
animals++;
}
}
printf( "%lld animals, %lld cats, %lld bigcats, %lld dogs\n", animals,cats,bigcats,dogs ) ;
}
//*NOTE: I changed from switch to if/else if chain
void regularSwitch(vector<Animal*> &zoo, ULONGLONG tests)
{
ULONGLONG animals=0,cats=0,bigcats=0,dogs=0;
for( ULONGLONG i = 0 ; i < tests ; i++ )
{
for( Animal* an : zoo )
{
if( an->typeTag & TypeDog )
dogs++;
else if( an->typeTag & TypeBigCat )
bigcats++;
else if( an->typeTag & TypeCat )
cats++;
else
animals++;
}
}
printf( "%lld animals, %lld cats, %lld bigcats, %lld dogs\n", animals,cats,bigcats,dogs ) ;
}
int main(int argc, const char * argv[])
{
vector<Animal*> zoo ;
zoo.push_back( new Animal ) ;
zoo.push_back( new Cat ) ;
zoo.push_back( new BigCat ) ;
zoo.push_back( new Dog ) ;
ULONGLONG tests=50000000;
dynamicCasts( zoo, tests ) ;
regularSwitch( zoo, tests ) ;
}