Почему компилятору C ++ не удалось встроить лямбду, переданную в шаблон функции? - PullRequest
0 голосов
/ 04 ноября 2019

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

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

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

template <typename F>
static inline void
hof(const F fun) {
    ...
    fun(a, b, c);     // a, b, c are int.
    ...
}
void
caller() {
    ...
    hof([&](int a, int b, int c) { ... });
    ...
}

Далее предположим, что недавно был включен gcc или clang со всеми соответствующими флагами оптимизации.

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

Я утверждаю, что (исключая "смешные дела", такие как исключения, longjmp, рефлексия и т. Д.) это невозможно сделать . Пожалуйста, попробуйте и докажите, что я неправ. Я приму любой ответ, для которого я смогу подтвердить с помощью godbolt.org, что лямбда не указана.

1 Ответ

1 голос
/ 04 ноября 2019

Это всего лишь вопрос заполнения достаточного количества материала в лямбду и его использования по крайней мере дважды (в противном случае нет веских оснований не указывать в строке).

Здесь для GCC 9.2 и Clang 9 с -O3:

#include<iostream>

int a, b, c;

template <typename F>
static inline void
hof(const F fun) {
    fun(a, b, c);
    fun(a, b, c);
}
void caller() {
    hof([&](int a, int b, int c) {
        std::cout << "Hello!";
        std::cout << "Hello!";
        std::cout << "Hello!";
        std::cout << "Hello!";
        std::cout << "Hello!";
        std::cout << "Hello!";
        std::cout << "Hello!";
        std::cout << "Hello!";
        std::cout << "Hello!";
        std::cout << "Hello!";
        std::cout << "Hello!";
        std::cout << "Hello!";
        std::cout << "Hello!";
        std::cout << "Hello!";
        std::cout << "Hello!";
        std::cout << "Hello!";
        std::cout << "Hello!";
        std::cout << "Hello!";
        std::cout << "Hello!";
        std::cout << "Hello!";
        std::cout << "Hello!";
        std::cout << "Hello!";
        std::cout << "Hello!";
        std::cout << "Hello!";
        std::cout << "Hello!";
        std::cout << "Hello!";
        std::cout << "Hello!";
        std::cout << "Hello!";
        std::cout << "Hello!";
    });
}

А сборка для caller выглядит следующим образом:

GCC:

caller():
        sub     rsp, 8
        call    caller()::{lambda(int, int, int)#1}::operator()(int, int, int) const [clone .isra.0]
        call    caller()::{lambda(int, int, int)#1}::operator()(int, int, int) const [clone .isra.0]
        add     rsp, 8
        ret

Clang:

caller():                             # @caller()
        push    rax
        call    caller()::$_0::operator()(int, int, int) const
        pop     rax
        jmp     caller()::$_0::operator()(int, int, int) const # TAILCALL

См. godbolt здесь .

Это ровно столько же повторений в лямбде, сколько мне нужно, чтобы убедить GCC, что вставка дважды не стоит.

Clang прекратил вставку уже с меньшим количествомповторы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...