Виртуальные функции и производительность - C ++ - PullRequest
112 голосов
/ 16 января 2009

В своем дизайне классов я широко использую абстрактные классы и виртуальные функции. У меня было ощущение, что виртуальные функции влияют на производительность. Это правда? Но я думаю, что эта разница в производительности не заметна и, похоже, я делаю преждевременную оптимизацию. Правильно?

Ответы [ 15 ]

2 голосов
/ 11 июля 2015

По моему опыту, главное - это способность встроить функцию. Если у вас есть потребность в производительности / оптимизации, которая требует указания функции, то вы не можете сделать функцию виртуальной, потому что это предотвратит это. В противном случае вы, вероятно, не заметите разницу.

2 голосов
/ 31 июля 2013

Я изучал это по крайней мере 20 раз в моем конкретном проекте. Хотя может иметь значительные преимущества в плане повторного использования кода, ясности, удобства сопровождения и читабельности, с другой стороны, с виртуальными функциями все еще существуют потери производительности do .

Будет ли заметным снижение производительности на современном ноутбуке / настольном компьютере / планшете ... вероятно, нет! Однако в некоторых случаях со встроенными системами снижение производительности может стать движущей силой неэффективности вашего кода, особенно если виртуальная функция вызывается снова и снова в цикле.

Вот документ с некоторыми датами, который анализирует лучшие практики для C / C ++ в контексте встроенных систем: http://www.open -std.org / jtc1 / sc22 / wg21 / docs / ESC_Boston_01_304_paper.pdf

В заключение: программист должен понять плюсы / минусы использования одной конструкции над другой. Если вы не сильно ориентированы на производительность, вам, вероятно, наплевать на производительность, и вам следует использовать все аккуратные OO-компоненты в C ++, чтобы сделать ваш код максимально удобным для использования.

1 голос
/ 16 декабря 2015

Стоит отметить, что это:

boolean contains(A element) {
    for (A current: this)
        if (element.equals(current))
            return true;
    return false;
}

может быть быстрее, чем это:

boolean contains(A element) {
    for (A current: this)
        if (current.equals(equals))
            return true;
    return false;
}

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

Я говорю "может", потому что это зависит от компилятора, кэша и т. Д.

0 голосов
/ 29 апреля 2011

Я всегда сомневался в этом, тем более, что - несколько лет назад - я также проводил такой тест, сравнивая время вызова стандартного метода-члена с виртуальным, и был очень зол на результаты в то время, имея пустой виртуальные вызовы в 8 раз медленнее, чем не виртуальные.

Сегодня я должен был решить, использовать ли виртуальную функцию для выделения большего объема памяти в моем классе буфера, в приложении, которое очень критично к производительности, поэтому я погуглил (и нашел вас), и в конце концов повторил тест .

// g++ -std=c++0x -o perf perf.cpp -lrt
#include <typeinfo>    // typeid
#include <cstdio>      // printf
#include <cstdlib>     // atoll
#include <ctime>       // clock_gettime

struct Virtual { virtual int call() { return 42; } }; 
struct Inline { inline int call() { return 42; } }; 
struct Normal { int call(); };
int Normal::call() { return 42; }

template<typename T>
void test(unsigned long long count) {
    std::printf("Timing function calls of '%s' %llu times ...\n", typeid(T).name(), count);

    timespec t0, t1;
    clock_gettime(CLOCK_REALTIME, &t0);

    T test;
    while (count--) test.call();

    clock_gettime(CLOCK_REALTIME, &t1);
    t1.tv_sec -= t0.tv_sec;
    t1.tv_nsec = t1.tv_nsec > t0.tv_nsec
        ? t1.tv_nsec - t0.tv_nsec
        : 1000000000lu - t0.tv_nsec;

    std::printf(" -- result: %d sec %ld nsec\n", t1.tv_sec, t1.tv_nsec);
}

template<typename T, typename Ua, typename... Un>
void test(unsigned long long count) {
    test<T>(count);
    test<Ua, Un...>(count);
}

int main(int argc, const char* argv[]) {
    test<Inline, Normal, Virtual>(argc == 2 ? atoll(argv[1]) : 10000000000llu);
    return 0;
}

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

0 голосов
/ 16 января 2009

Потеря производительности при использовании виртуальных функций никогда не может перевесить преимущества, которые вы получаете на уровне разработки. Предположительно, вызов виртуальной функции будет на 25% менее эффективным, чем прямой вызов статической функции. Это потому, что существует уровень косвенности через VMT. Однако время, необходимое для выполнения вызова, обычно очень мало по сравнению с временем, затрачиваемым на фактическое выполнение вашей функции, поэтому общие затраты на производительность будут незначительными, особенно с текущей производительностью оборудования. Кроме того, компилятор может иногда оптимизировать и видеть, что виртуальный вызов не требуется, и компилировать его в статический вызов. Так что не волнуйтесь, используйте виртуальные функции и абстрактные классы столько, сколько вам нужно.

...