Dlaczego lambdy mogą być lepiej zoptymalizowane przez kompilator niż zwykłe funkcje?

W swojej książce The C++ Standard Library (Second Edition) Nicolai Josuttis stwierdza, że lambda mogą być lepiej zoptymalizowane przez kompilator niż zwykłe funkcje.

Ponadto kompilatory C++ optymalizują lambda lepiej niż zwykłe funkcje. (Strona 213)

Dlaczego?

Myślałem, że jeśli chodzi o inlining, nie powinno być już żadnej różnicy. Jedynym powodem, dla którego mogłem myśleć jest to, że Kompilatory mogą mieć lepszy lokalny kontekst z lambda i takie mogą tworzyć więcej założeń i wykonać więcej optymalizacji.

Author: Mysticial, 2012-12-05

2 answers

Powodem jest to, że lambda są obiektami funkcyjnymi , więc przekazanie ich do szablonu funkcji spowoduje utworzenie nowej funkcji specjalnie dla tego obiektu. Kompilator może więc trywialnie wbudować wywołanie lambda.

Dla funkcji, z drugiej strony, stosuje się stare zastrzeżenie: funkcja Wskaźnik jest przekazywana do szablonu funkcji, a Kompilatory tradycyjnie mają wiele problemów z wywołaniem za pomocą wskaźników funkcji. Mogą teoretycznie być inlined, ale tylko wtedy, gdy funkcja otoczenia jest inlined, jak również.

Jako przykład rozważ następujący szablon funkcji:

template <typename Iter, typename F>
void map(Iter begin, Iter end, F f) {
    for (; begin != end; ++begin)
        *begin = f(*begin);
}

Nazywając to lambdą w ten sposób:

int a[] = { 1, 2, 3, 4 };
map(begin(a), end(a), [](int n) { return n * 2; });

Wyniki tej instancji (stworzonej przez kompilator):

template <>
void map<int*, _some_lambda_type>(int* begin, int* end, _some_lambda_type f) {
    for (; begin != end; ++begin)
        *begin = f.operator()(*begin);
}

... kompilator zna _some_lambda_type::operator () i potrafi wywoływać do niego trywialnie. (I wywołanie funkcji map z dowolnym innym lambda utworzyłoby nową instancję map ponieważ każda lambda ma odrębną Typ.)

Ale gdy wywołane ze wskaźnikiem funkcji, instancja wygląda następująco:

template <>
void map<int*, int (*)(int)>(int* begin, int* end, int (*f)(int)) {
    for (; begin != end; ++begin)
        *begin = f(*begin);
}

... i tutaj f wskazuje na inny adres dla każdego wywołania do map i dlatego kompilator nie może wywoływać inline do f, chyba że wywołanie otaczające do map zostało również wbudowane tak, że kompilator może rozwiązać f do jednej konkretnej funkcji.

 157
Author: Konrad Rudolph,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-05 14:03:44

Ponieważ kiedy przekazujesz" funkcję " do algorytmu, w rzeczywistości przekazujesz wskaźnik do funkcji, więc musi ona wykonać pośrednie wywołanie za pomocą wskaźnika do funkcji. Kiedy używasz lambda, przekazujesz obiekt do instancji szablonu specjalnie stworzonej dla tego typu, A wywołanie funkcji lambda jest wywołaniem bezpośrednim, a nie wywołaniem za pomocą wskaźnika funkcji, więc może być znacznie bardziej prawdopodobne, że zostanie zainlinowane.

 23
Author: jcoder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-11 22:21:33