Я думаю, вы видите разницу, но это просто накладные расходы на вызов функции. Неправильное предсказание ветвления, доступ к памяти и функции триггера одинаковы в обоих случаях. По сравнению с этим, это не так уж и сложно, хотя случай с указателем на функцию определенно был быстрее, когда я попробовал.
Если это представитель вашей более крупной программы, это хорошая демонстрация того, что этот тип микрооптимизации иногда является просто каплей в океане, а в худшем случае бесполезен. Но, оставив это в стороне, для более четкого теста функции должны выполнять более простую операцию, отличающуюся для каждой функции:
void function_not( double *d ) {
*d = 1.0;
}
void function_and( double *d ) {
*d = 2.0;
}
И т. Д., И аналогично для виртуальных функций.
(Каждая функция должна делать что-то свое, чтобы они не были исключены и все получили один и тот же адрес; это сделало бы предсказание ветвления нереально хорошим.)
С этими изменениями результаты немного отличаются. Лучший из 4 трасс в каждом случае. (Не очень научно, но цифры в целом схожи для большего количества прогонов.) Все таймеры работают на моем ноутбуке циклично. Код был скомпилирован с использованием VC ++ (только изменил время), но gcc реализует вызовы виртуальных функций таким же образом, поэтому относительные временные параметры должны быть примерно одинаковыми даже с различными процессорами / компиляторами OS / x86.
Функциональные указатели: 2,052,770
Виртуалы: 3 598 039
Эта разница кажется немного чрезмерной! Конечно же, два бита кода не совсем одинаковы с точки зрения поведения доступа к памяти. Второй должен иметь таблицу 4 A * s, используемую для заполнения базы, а не для создания новой для каждой записи. В этом случае оба примера будут иметь одинаковое поведение (1 промах кэша / N записей) при получении указателя для перехода. Например:
A *tbl[4] = { new A1, new A2, new A3, new A4 };
for ( long int i = 0; i < 100000; ++i ) {
array[i] = ( double )( rand() / 1000 );
base[i] = tbl[ rand() % 4 ];
}
Имея это, все еще используя упрощенные функции:
Виртуалы (как предлагается здесь): 2 487 699
Так что 20%, лучший случай. Достаточно близко?
Так что, возможно, ваш коллега был прав, по крайней мере, подумать об этом, но я подозреваю, что в любой реалистичной программе издержки вызова не будут достаточным узким местом, чтобы стоило перепрыгивать через обручи.