C ++ 11 Производительность: лямбда-инлайн против специализации шаблонов функций - PullRequest
0 голосов
/ 08 февраля 2019

Мой вопрос заключается в следующем: Почему лямбды могут быть лучше оптимизированы компилятором, чем обычные функции?

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

int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }

template <class F>
int operate(int a, int b, F func)
{
    return func(a, b);
}

template <int func(int, int)>
int operateFuncTemplate(int a, int b)
{
    return func(a, b);
}

int main()
{
    // hard to inline (can't determine statically if operate's f is add or sub since its just a function pointer)
    auto addWithFuncP = operate(1, 2, add);
    auto subWithFuncP = operate(1, 2, sub);

    // easy to inline (lambdas are unique so 2 specializations made, each easy to inline)
    auto addWithLamda = operate(1, 2, [](int a, int b) { return a + b; });
    auto subWithLamda = operate(1, 2, [](int a, int b) { return a - b; });

    // also easy to inline? specialization means there are 2 made, instead of just 1 function definition with indirection?
    auto addWithFuncT = operateFuncTemplate<add>(1, 2);
    auto subWithFuncT = operateFuncTemplate<sub>(1, 2);
}

Итак, если бы я мог ранжировать их по шкале производительности, тогда:

operatorFuncTemplate> = operate<LAMBDA>> = operate<FUNCTIONPTR>

Существуют ли случаи, когда это отношение может не срабатывать в нетривиальных примерах?

1 Ответ

0 голосов
/ 08 февраля 2019

Если компилятор может отслеживать «указатель на эту функцию указывает на эту функцию», компилятор может встроить вызов через указатель на функцию.

Иногда компиляторы могут сделать это.Иногда они не могут.

Если вы не храните лямбду в указателе функции, std::function или подобной обертке для стирания типов, то компилятор в точке, где вызывается лямбда, знает тип лямбды, поэтому он знаеттело лямбды.Компилятор может тривиально встроить вызов функции.

Ничто в использовании шаблона функции не меняет этого, кроме случаев, когда аргумент constexpr подобен параметру шаблона типа функции:

template <int func(int, int)>

это пример этого.Здесь шаблон функции в теле функции гарантированно будет известен во время компиляции.

Однако, передайте этот func где-нибудь еще, и компилятор может потерять его.

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

Любые универсальные утверждения, которые вы пытаетесь сделать, идутиногда ошибаться.

...