c++ - vorteil - Warum können lambdas vom Compiler besser optimiert werden als einfache Funktionen?




python lambda funktionen (2)

In seinem Buch The C++ Standard Library (Second Edition) Nicolai Josuttis fest, dass Lambdas vom Compiler besser optimiert werden können als einfache Funktionen.

Außerdem optimieren C ++ - Compiler Lambdas besser als normale Funktionen. (Seite 213)

Warum das?

Ich dachte, wenn es um Inlining geht, sollte es keinen Unterschied mehr geben. Der einzige Grund, an den ich denken könnte, ist, dass Compiler mit lambdas einen besseren lokalen Kontext haben und so mehr Annahmen treffen und mehr Optimierungen vornehmen können.


Denn wenn Sie eine "Funktion" an einen Algorithmus übergeben, übergeben Sie tatsächlich einen Zeiger an die Funktion, so dass er einen indirekten Aufruf über den Zeiger auf die Funktion ausführen muss. Wenn Sie ein Lambda verwenden, übergeben Sie ein Objekt an eine Vorlageninstanz, die speziell für diesen Typ instanziiert wurde, und der Aufruf der Lambda-Funktion ist ein direkter Aufruf, kein Aufruf über einen Funktionszeiger, so dass es viel wahrscheinlicher inline sein kann.


Der Grund dafür ist, dass Lambdas Funktionsobjekte sind. Wenn Sie sie also an eine Funktionsvorlage übergeben, wird eine neue Funktion speziell für dieses Objekt instanziiert. Der Compiler kann somit den Lambda-Aufruf trivial einfügen.

Für Funktionen dagegen gilt der alte Caveat: Ein Funktionszeiger wird an die Funktionsschablone weitergegeben, und Compiler haben traditionell viele Probleme, Aufrufe über Funktionszeiger anzuordnen. Sie können theoretisch inline sein, aber nur, wenn die umgebende Funktion ebenfalls inline ist.

Betrachten Sie als Beispiel die folgende Funktionsvorlage:

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

Nenne es mit einem Lambda wie folgt:

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

Ergebnisse in dieser Instanziierung (vom Compiler erstellt):

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

... der Compiler kennt _some_lambda_type::operator () und kann diese inline _some_lambda_type::operator () . (Und das Aufrufen der Funktionszuordnung mit einem anderen Lambda würde eine neue Instanz der map erstellen, da jedes Lambda einen unterschiedlichen Typ hat.)

Wenn sie jedoch mit einem Funktionszeiger aufgerufen wird, sieht die Instanziierung wie folgt aus:

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

... und hier zeigt f auf eine andere Adresse für jeden zu mappenden Aufruf, und daher kann der Compiler keine Inline-Aufrufe an f ausführen, es sei denn, der umgebende Call to map wurde ebenfalls inline, so dass der Compiler f zu einer bestimmten Funktion auflösen kann.





compiler-optimization