динамические издержки в C ++ - PullRequest
11 голосов
/ 08 сентября 2011

Я знаю, что у dynamic_cast есть проверка во время выполнения, и поэтому он считается более безопасным (может вернуть нулевой указатель при сбое), но медленнее, чем static_cast.но насколько плохи накладные расходы между этими двумя?

я должен действительно рассмотреть использование static_cast в циклах для проблем производительности в обычных больших проектах?или разница незначительна и актуальна только для специальных программ реального времени.

Ответы [ 5 ]

11 голосов
/ 08 сентября 2011

Вы профилировали это?

Правило:

  • Используйте static_cast, когда вы знаете , что целевой тип действителен.
  • Используйте dynamic_cast, когда вы не уверены, и вам нужна программа, чтобы найти для вас тип времени выполнения объекта.

Это так просто.Все остальные соображения вторичны.

5 голосов
/ 08 сентября 2011

Зависит от того, как динамическое приведение выполняет проверку безопасности / корректности класса.В системах, которые я профилировал, он может очень быстро превратиться в большое количество строк.Это достаточно большое дело, поэтому мы в значительной степени используем систему стилей assert_cast, в которой статическое приведение выполняется для повышения производительности, а динамическое - для отладки.

2 голосов
/ 08 сентября 2011

Томалак Герет'кал прав, используйте static_cast, когда знаете, dynamic_cast, когда нет.Если вы хотите избежать затрат, вы должны структурировать свой дизайн таким образом, чтобы вы знали.Хранение отдельных типов в отдельных контейнерах сделает вашу логику цикла более сложной, но вы можете исправить это с помощью шаблонных алгоритмов.

Для простых деревьев наследования это довольно быстро.Если вы выполняете боковое преобразование в сложной иерархии с виртуальным наследованием, тогда он должен выполнять нетривиальный поиск.

Примеры:

struct Base {virtual ~Base () {}};
struct Foo : Base {};

struct Bar1 : virtual Base {};
struct Bar2 : virtual Base {};

struct Baz : Bar1, Bar2 {};

Base * a = new Foo ();
Bar1 * b = new Baz ();

dynamic_cast <Foo *> (a); // fast
dynamic_cast <Bar2 *> (b); // slow

Производительность во многом зависит от компилятора.Мера, мера, мера!Имейте в виду, что информация о типах времени выполнения, как правило, вычитается и хранится в нелокальной памяти - вы должны учитывать, что будет делать кэш в циклах.

0 голосов
/ 08 сентября 2011

Я только что опробовал небольшой эталон кастов (на моем ~ 3-летнем нетбуке, так что цифры довольно высокие, но хорошо).Это тестовая установка:

class A {
  public:
    virtual ~A() {}
};

class B : public A {
};

#define IT(DO) \
    for (unsigned i(1<<30); i; i--) { \
      B* volatile b(DO); \
      (void)b; \
    }

#define CastTest(CAST) IT(CAST<B*>(a))
#define NullTest() IT(NULL)

int main(int argc, char** argv) {
  if (argc < 2) {
    return 1;
  }
  A* a(new B());
  switch (argv[1][0]) {
    case 'd':
      CastTest(dynamic_cast)
      break;
    case 's':
      CastTest(static_cast)
      break;
    default:
      NullTest()
      break;
  }
  return 0;
}

Я обнаружил, что она сильно зависит от оптимизации компилятора, поэтому вот мои результаты:

(см. Оценка ниже)

O0:

g++ -O0 -Wall castbench.cpp; time ./a.out _; time ./a.out s; time ./a.out d

real        0m7.139s
user        0m6.112s
sys         0m0.044s

real        0m8.177s
user        0m6.980s
sys         0m0.024s

real        1m38.107s
user        1m23.929s
sys         0m0.188s

O1:

g++ -O1 -Wall castbench.cpp; time ./a.out _; time ./a.out s; time ./a.out d

real        0m4.412s
user        0m3.868s
sys         0m0.032s

real        0m4.653s
user        0m4.048s
sys         0m0.000s

real        1m33.508s
user        1m21.209s
sys         0m0.236s

O2:

g++ -O2 -Wall castbench.cpp; time ./a.out _; time ./a.out s; time ./a.out d

real        0m4.526s
user        0m3.960s
sys         0m0.044s

real        0m4.862s
user        0m4.120s
sys         0m0.004s

real        0m2.835s
user        0m2.548s
sys         0m0.008s

O3:

g++ -O3 -Wall castbench.cpp; time ./a.out _; time ./a.out s; time ./a.out d

real        0m4.896s
user        0m4.308s
sys         0m0.004s

real        0m5.032s
user        0m4.284s
sys         0m0.008s

real        0m4.828s
user        0m4.160s
sys         0m0.008s

Редактировать: Оценка

Для одного приведения (в приведенном выше тесте у нас было 2**30 приведений) мы получаем следующие моменты в минимальном примере выше:

-O0    71.66 ns
-O1    71.86 ns
-O2    -1.46 ns
-O3    -0.11 ns

Отрицательные значения, вероятно, связаны с различными нагрузками в момент выполнения программы и достаточно малы, чтобы их можно было отбросить как незначительные (т. Е. == 0).Поскольку здесь нет никаких накладных расходов, мы должны предположить, что компилятор был достаточно умен, чтобы оптимизировать отбрасывание, даже если мы сказали, что b был изменчивым.Следовательно, единственными надежными значениями являются результаты 70 нс.

0 голосов
/ 08 сентября 2011

Чрезвычайно большие кодовые базы C ++ (например, Mozilla, OpenOffice) имеют привычку отключать RTTI (и, следовательно, не могут использовать dynamic_cast и исключения), поскольку накладные расходы составляют всего , включая данные RTTI в исполняемый файл считается неприемлемым. В частности, сообщается, что он вызывает значительное (я помню цифры порядка 10%) время запуска из-за дополнительных динамических перемещений.

Вопрос о том, является ли дополнительный код, необходимый для исключения dynamic_cast и исключений, даже медленнее, никогда не обсуждается.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...