Почему разрешение метода во время выполнения быстрее, чем разрешение во время компиляции? - PullRequest
1 голос
/ 28 мая 2010

В школе у ​​нас есть virtual функции в C ++, и как они разрешены (или найдено , или соответствует , я не знаю какова терминология - мы не изучаем английский язык) во время выполнения, а не во время компиляции. Учитель также сказал нам, что разрешение во время компиляции намного быстрее, чем во время выполнения (и это будет иметь смысл). Тем не менее, быстрый эксперимент предполагает иное. Я построил эту маленькую программу:

#include <iostream>
#include <limits.h>

using namespace std;

class A {
    public:
    void f() {
        // do nothing
    }
};

class B: public A {
    public:
    void f() {
        // do nothing
    }
};

int main() {
    unsigned int i;
    A *a = new B;
    for (i=0; i < UINT_MAX; i++) a->f();
    return 0;
}

Я скомпилировал программу выше и назвал ее normal. Затем я изменил A, чтобы он выглядел так:

class A {
    public:
    virtual void f() {
        // do nothing
    }
};

Скомпилировано и названо virtual. Вот мои результаты:

[felix@the-machine C]$ time ./normal 

real    0m25.834s
user    0m25.742s
sys 0m0.000s
[felix@the-machine C]$ time ./virtual 

real    0m24.630s
user    0m24.472s
sys 0m0.003s
[felix@the-machine C]$ time ./normal 

real    0m25.860s
user    0m25.735s
sys 0m0.007s
[felix@the-machine C]$ time ./virtual 

real    0m24.514s
user    0m24.475s
sys 0m0.000s
[felix@the-machine C]$ time ./normal 

real    0m26.022s
user    0m25.795s
sys 0m0.013s
[felix@the-machine C]$ time ./virtual 

real    0m24.503s
user    0m24.468s
sys 0m0.000s

Кажется, что разница в пользу виртуальной версии составляет ~ 1 секунду. Почему это?


Соответствует или нет: двухъядерный Pentium @ 2,80 ГГц, никаких дополнительных приложений, работающих между двумя тестами. Archlinux с gcc 4.5.0. Компилируется нормально, например:

$ g++ test.cpp -o normal

Кроме того, -Wall также не выдает никаких предупреждений.


Редактировать: Я разделил свою программу на A.cpp, B.cpp и main.cpp. Кроме того, я сделал функцию f()A::f(), и B::f()) на самом деле do что-то (x = 0 - x, где x - это public int член A, инициализируется с 1 в A::A()). Скомпилировал это в шесть версий, вот мои окончательные результаты:

[felix@the-machine poo]$ time ./normal-unoptimized 

real    0m31.172s
user    0m30.621s
sys 0m0.033s
[felix@the-machine poo]$ time ./normal-O2

real    0m2.417s
user    0m2.363s
sys 0m0.007s
[felix@the-machine poo]$ time ./normal-O3

real    0m2.495s
user    0m2.447s
sys 0m0.000s
[felix@the-machine poo]$ time ./virtual-unoptimized 

real    0m32.386s
user    0m32.111s
sys 0m0.010s
[felix@the-machine poo]$ time ./virtual-O2

real    0m26.875s
user    0m26.668s
sys 0m0.003s
[felix@the-machine poo]$ time ./virtual-O3

real    0m26.905s
user    0m26.645s
sys 0m0.017s

Unoptimized по-прежнему на 1 секунду быстрее, чем виртуальный, что я нахожу немного странным. Но это был хороший эксперимент, и я хотел бы поблагодарить всех вас за ваши ответы!

Ответы [ 4 ]

11 голосов
/ 28 мая 2010

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

6 голосов
/ 28 мая 2010

Профилирование неоптимизированного кода в значительной степени бессмысленно. Используйте -O2 для получения значимого результата. Использование -O3 может привести к еще более быстрому коду, но может не дать реалистичного результата, если вы не скомпилируете A::f и B::f отдельно в main (т.е. в отдельных единицах компиляции).

Судя по отзывам, возможно, даже -O2 слишком агрессивен. Результат за 2 мс, потому что компилятор полностью оптимизировал цикл. Прямые звонки не , что быстро; на самом деле, должно быть очень трудно наблюдать заметную разницу. Переместите реализации f в отдельный модуль компиляции, чтобы получить действительные числа. Определите классы в .h, но определите A::f и B::f в их собственном файле .cc.

2 голосов
/ 28 мая 2010

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

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

1 голос
/ 28 мая 2010

Учитывая простоту программы, вполне вероятно, что компилятор оптимизирует определенные вещи или что-то в этом роде. Добавление сложности / создание компилятором именно того, чего вы хотите, - это то, к чему вы должны стремиться с помощью такого рода вещей (во время выполнения разница, как мне кажется, всего 2 разыменования, так что меньше, чем у остальной части вызова функции). Один из способов сделать это, как сказал @Marcelo, компилировать A и B в отдельных файлах из main - я бы пошел дальше и скомпилировал каждый из них в свой собственный файл. Однако я не согласен с ним в том, что по причинам, указанным выше, вы должны отключить оптимизацию, чтобы компилятор выполнил буквальный перевод вашего кода и не удалил ничего.

...