Хорошо известное решение, позволяющее избежать медлительности dynamic_cast?

Мне нужен был полиморфизм времени выполнения, поэтому я использовал dynamic_cast,
Но теперь у меня было две проблемы — dynamic_cast было очень медленно! (Прокрутите вниз для сравнения.)

Короче говоря, я решил проблему таким образом, используя static_cast:

struct Base
{
virtual ~Base() { }
virtual int type_id() const = 0;

template<class T>
T *as()
{ return this->type_id() == T::ID ? static_cast<T *>(this) : 0; }

template<class T>
T const *as() const
{ return this->type_id() == T::ID ? static_cast<T const *>(this) : 0; }
};

struct Derived : public Base
{
enum { ID = __COUNTER__ };  // warning: can cause ABI incompatibility
int type_id() const { return ID; }
};

int main()
{
Base const &value = Derived();
Derived const *p = value.as<Derived>();  // "static" dynamic_cast
}

Но я определенно не первый, кто сталкивается с этой проблемой, поэтому я подумал, что стоит спросить:

Вместо того, чтобы придумать решение, выпеченное в домашних условиях, как это, есть ли известный шаблон / библиотека, которую я могу использовать для решения этой проблемы в будущем?


Образец теста

Чтобы понять, о чем я говорю, попробуйте код ниже — dynamic_cast был примерно 15 раз медленнее, чем просто virtual Позвоните на мою машину (110 мс против 1620 мс с кодом ниже):

#include <cstdio>
#include <ctime>

struct Base { virtual unsigned vcalc(unsigned i) const { return i * i + 1; } };
struct Derived1 : public Base
{ unsigned vcalc(unsigned i) const { return i * i + 2; } };
struct Derived2 : public Derived1
{ unsigned vcalc(unsigned i) const { return i * i + 3; } };

int main()
{
Base const &foo = Derived2();
size_t const COUNT = 50000000;
{
clock_t start = clock();
unsigned n = 0;
for (size_t i = 0; i < COUNT; i++)
n = foo.vcalc(n);
printf("virtual call: %d ms (result: %u)\n",
(int)((clock() - start) * 1000 / CLOCKS_PER_SEC), n);
fflush(stdout);
}
{
clock_t start = clock();
unsigned n = 0;
for (size_t i = 0; i < COUNT; i++)
n = dynamic_cast<Derived1 const &>(foo).vcalc(n);
printf("virtual call after dynamic_cast: %d ms (result: %u)\n",
(int)((clock() - start) * 1000 / CLOCKS_PER_SEC), n);
fflush(stdout);
}
return 0;
}

Когда я просто удаляю слово virtual и изменить dynamic_cast в static_castЯ получаю время выполнения 79 мс — так что виртуальный вызов медленнее статического на ~ 25%!

5

Решение

Большинство применений dynamic_cast может быть заменен двойной отправкой (ака. шаблон посетителя). Это равняется двум виртуальным вызовам, которые по вашему тесту все еще в 7,5 раз быстрее, чем dynamic_cast,

3

Другие решения

Вас может заинтересовать эта постоянная реализация времени: http://www.stroustrup.com/isorc2008.pdf

Также обратите внимание, что многие восходящие передачи могут быть упрощены при определенных ограничениях — например, если вы не используете множественное наследование, используете только поверхностное наследование или иным образом гарантируете, что у типа не будет общих предков, некоторые оценки могут быть короткими и исчерпывающими оценка (как предусмотрено dynamic_cast) не нужно выполнять.

Как обычно, профилируйте свою реализацию в соответствии с реализацией вашего вендора для данного варианта использования и фактических иерархий классов.

2

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector