Где-то пару-тройку лет назад проверял performance hit при переходе от free functions к виртуальным методам класса, и затем к boost::function в связке с boost::bind (такая замена сильно упрощала код). На тот момент замедление было существенным — вместо 1 нс на вызов простой функции уходило 20 нс (gcc 4.2 или как-то так).
К сожалению, код того теста остался на том месте работы.
Набросал новый, примерно такой (разумеется, непортабельный):
И результаты меня даже слегка удивили.
Во-первых, вызов виртуального метода стабильно быстрее вызова обычного. Во-вторых, начиная с длины строки где-то в размер одной строки кэша (16 символов) разницы с std::bind нет. Да и в целом std::function + std::bind чертовски быстры, даже на пустой строке дают не более 10% замедления.
В общем, я заподозрил, что где-то в чем-то ошибся в тесте. Часы (clock) — HPET, т.е. замеры корректны. Опять же, понимаю, что тут упирается в работу с памятью, но все равно уж больно мал порядок расхождения. Проверял на рвзных машинах, соотношение примерно одинаковое везде.
Может, кто-нибудь мне подскажет, где найти более осмысленные тесты? Интересует сравнение free function vs. virtual function vs. functor vs. std::function + std::bind.
Здравствуйте, SkyDance, Вы писали:
SD>Во-первых, вызов виртуального метода стабильно быстрее вызова обычного. Во-вторых, начиная с длины строки где-то в размер одной строки кэша (16 символов) разницы с std::bind нет. Да и в целом std::function + std::bind чертовски быстры, даже на пустой строке дают не более 10% замедления. SD>В общем, я заподозрил, что где-то в чем-то ошибся в тесте. Часы (clock) — HPET, т.е. замеры корректны. Опять же, понимаю, что тут упирается в работу с памятью, но все равно уж больно мал порядок расхождения. Проверял на рвзных машинах, соотношение примерно одинаковое везде. SD>Может, кто-нибудь мне подскажет, где найти более осмысленные тесты? Интересует сравнение free function vs. virtual function vs. functor vs. std::function + std::bind.
Если тело ф-ции хоть сколько-нибудь осмысленное и вы не гоняете это дело в цикле, то абсолютно нет никакой разницы как вы это все вызываете. А значит подобные замеры не имеют практической ценности.
Ну а если все-таки у вас нагружненный, то имеет смысл изучать конкретную ситуацию, а не анализировать абстрактный call в вакууме.
_>Если тело ф-ции хоть сколько-нибудь осмысленное и вы не гоняете это дело в цикле, то абсолютно нет никакой разницы как вы это все вызываете. А значит подобные замеры не имеют практической ценности.
Разумеется, это гоняется в цикле. Изначально это были strand'ы от boost::asio, и прочие handler'ы, с широким и массовым использованием boost::function & boost::bind.
_>Ну а если все-таки у вас нагружненный, то имеет смысл изучать конкретную ситуацию, а не анализировать абстрактный call в вакууме.
Видимо, мне стоило упомянуть это в начальном сообщении (или даже дать ссылку на предыдущий тред, но я что-то не нашел его), что такой анализ уже проводился, и одним из узких мест как раз стал boost::asio и его handler'ы, и среди них — именно массовые вызовы функций через boost::function и boost::bind.
Три года назад пришлось переписать без использования asio (а также function & bind). Сейчас натолкнулся на похожую ситуацию, и решил перетестировать, изменилось ли что-нибудь за 3 с лишним года — все же C++ 11 стал хорошо поддерживаться в gcc.
Очень многие вещи изменились радикально (вот хотя бы те же per-instance allocators в STL).
SD>И результаты меня даже слегка удивили. SD>Во-первых, вызов виртуального метода стабильно быстрее вызова обычного. Во-вторых, начиная с длины строки где-то в размер одной строки кэша (16 символов) разницы с std::bind нет. Да и в целом std::function + std::bind чертовски быстры, даже на пустой строке дают не более 10% замедления. SD>В общем, я заподозрил, что где-то в чем-то ошибся в тесте. Часы (clock) — HPET, т.е. замеры корректны. Опять же, понимаю, что тут упирается в работу с памятью, но все равно уж больно мал порядок расхождения. Проверял на рвзных машинах, соотношение примерно одинаковое везде. SD>Может, кто-нибудь мне подскажет, где найти более осмысленные тесты? Интересует сравнение free function vs. virtual function vs. functor vs. std::function + std::bind.
Посмотрел дизасемблер на gcc 4.8.1 + amd64.
Все кроме вызовы кроме bind превратились в вызовы
по статическому адресу, т.е.
callq константа
а вызов с помощью bind превратился в
callq *%rax
Так что ошибка в недооценке оптимизатора.
Виртуальную функцию стоит вызывать с помощью указателя на базовый класс,
в тестирующую функцию передавать указатель на базовый класс, и иницилизировать
этот указатель с помощью функции находящейся в другой единице трансляции (LTO в этом случае не использовать), или
объявить указатель volatile.