В качестве альтернативы BaseThing может иметь чисто виртуальный GetID()
, который будет использоваться для определения типа вместо использования typeid.В этой ситуации, с только 1 уровнем наследования, какова стоимость typeid против стоимости вызова виртуальной функции?Я знаю, что typeid каким-то образом использует vtable, но как именно он работает?
В Linux и Mac или что-то еще, использующее ABI Itanium C ++, typeid(x)
компилируется в две инструкции загрузки - он просто загружаетvptr (то есть адрес некоторой виртуальной таблицы) из первых 8 байтов объекта x
, а затем загружает -1
-й указатель из этой виртуальной таблицы.Этот указатель &typeid(x)
.Это на один вызов функции дешевле дороже, чем вызов виртуального метода.
В Windows он включает в себя порядка четырех инструкций загрузки и пару (незначительных)ALU ops, потому что Microsoft C ++ ABI немного более корпоративный .( source ) Честно говоря, это может оказаться на уровне виртуального вызова метода.Но это все еще очень дешево по сравнению с dynamic_cast
.
A dynamic_cast
включает вызов функции в среду выполнения C ++, которая имеет lot загрузок и условных ветвей и тому подобное.
Так что да, использование typeid
будет намного быстрее, чем dynamic_cast
.Это будет правильный для вашего варианта использования? - это сомнительно.(См. Другие ответы о заменяемости по Лискову и тому подобное.) Но это будет быстро? - да.
Здесь я взял код теста игрушек из высоко оцененного ответа Вона и сделал егов фактический бенчмарк , избегая очевидной оптимизации подъема петли, которая мешала ему все время.Результат для libc ++ abi на моем Macbook:
$ g++ test.cc -lbenchmark -std=c++14; ./a.out
Run on (4 X 2400 MHz CPU s)
2017-06-27 20:44:12
Benchmark Time CPU Iterations
---------------------------------------------------------
bench_dynamic_cast 70407 ns 70355 ns 9712
bench_typeid 31205 ns 31185 ns 21877
bench_id_method 30453 ns 29956 ns 25039
$ g++ test.cc -lbenchmark -std=c++14 -O3; ./a.out
Run on (4 X 2400 MHz CPU s)
2017-06-27 20:44:27
Benchmark Time CPU Iterations
---------------------------------------------------------
bench_dynamic_cast 57613 ns 57591 ns 11441
bench_typeid 12930 ns 12844 ns 56370
bench_id_method 20942 ns 20585 ns 33965
(чем меньше ns
, тем лучше. Вы можете игнорировать два последних столбца: «CPU» просто показывает, что он все время работает, и нетвремя ожидания, а «Итерации» - это просто количество прогонов, которое потребовалось, чтобы получить хороший предел погрешности.)
Вы можете видеть, что typeid
перебивает dynamic_cast
даже при -O0
, но когда вывключите оптимизацию, это работает даже лучше - потому что компилятор может оптимизировать любой код, который you пишет. Весь этот уродливый код, спрятанный внутри libc ++, функция __dynamic_cast
abi не может быть оптимизирована компилятором так, как это уже было, поэтому включение -O3
не сильно помогло.