web-dev-qa-db-fra.com

Les lambdas sont-ils alignés comme des fonctions en C ++?

Le compilateur peut-il/fonctionne-t-il des fonctions lambda pour augmenter l'efficacité, comme il pourrait le faire avec des fonctions standard simples?

par exemple.

std::vector<double> vd;
std::for_each(vd.begin(), vd.end(), [](const double d) {return d*d;});

Ou y a-t-il une perte d'efficacité causée par un manque d'optimisation?

Une deuxième question: où puis-je vérifier si le compilateur que j'utilise a des appels optimisés de fonctions en ligne, qui sont envoyées à un algorithme? Ce que je veux dire, c'est que si une fonction - pas un objet fonction - est envoyée à un algorithme, le dernier obtient un pointeur vers la fonction, et certains compilateurs optimisent les pointeurs vers les fonctions en ligne et d'autres pas.

43
Illia Levandovskyi

Dans des cas simples, comme votre exemple, vous devriez vous attendre à de meilleures performances avec les lambdas qu'avec les pointeurs de fonction, voir

Pourquoi les lambdas peuvent-ils être mieux optimisés par le compilateur que les fonctions simples?

Comme d'autres l'ont déjà souligné, rien ne garantit que votre appel sera en ligne, mais vous avez de meilleures chances avec les lambdas. Une façon de vérifier si l'appel a été intégré est de vérifier le code généré. Si vous utilisez gcc, passez l'indicateur -S au compilateur. Bien sûr, cela suppose que vous pouvez comprendre le code Assembly.



Mise à jour du 11 septembre 2018: Vipul Kumar a souligné deux drapeaux de compilation dans sa modification.

GCC -Winline

Avertir si une fonction déclarée inline ne peut pas être insérée. Même avec cette option, le compilateur ne met pas en garde contre les échecs des fonctions en ligne déclarées dans les en-têtes du système.

Le compilateur utilise une variété d'heuristiques pour déterminer s'il faut ou non incorporer une fonction. Par exemple, le compilateur prend en compte la taille de la fonction en cours de mise en ligne et la quantité de mise en ligne qui a déjà été effectuée dans la fonction en cours. Par conséquent, des changements apparemment insignifiants dans le programme source peuvent faire apparaître ou disparaître les avertissements produits par -Winline.

Si je comprends bien, si votre fonction n'est pas déclarée en ligne, cet indicateur de compilation est très probablement pas utile. Néanmoins, il est bon de savoir qu'il existe et cela répond en partie à votre deuxième question.

L'autre drapeau qu'il a souligné est:

Clang -Rpass=inline

Options pour émettre des rapports d'optimisation

Les rapports d'optimisation retracent, à un niveau élevé, toutes les décisions majeures prises par les transformations du compilateur. Par exemple, lorsque l'inliner décide d'inclure la fonction foo () dans bar () [...]

Je n'ai pas utilisé celui-ci moi-même, mais sur la base de la documentation, il pourrait être utile pour votre cas d'utilisation.

Je vérifie personnellement l'assembly généré chaque fois que cela est si important.

34
Ali

Tout d'abord: l'intérêt de la conception des lambdas en C++ est qu'ils n'ont pas de surcharge par rapport aux appels de fonction. Cela inclut notamment le fait que les appels à eux peuvent être intégrés.

Mais il y a une confusion de concepts ici: dans la norme C++, "inline" est le lien d'une fonction, c'est-à-dire qu'il s'agit d'une instruction sur la façon dont une fonction est définie , pas comment elle est appelée. Les fonctions définies en ligne peuvent bénéficier d'une optimisation du compilateur par laquelle les appels à ces fonctions sont en ligne. C’est un concept différent mais très lié.

Dans le cas de lambdas, la fonction réelle appelée est un membre operator() qui est implicitement défini comme inline dans une classe anonyme créée par le compilateur pour le lambda. Les appels du lambda sont traduits en appels directs vers sa operator() et peuvent donc être alignés. J'ai expliqué comment le compilateur crée des types lambda plus en détail dans une autre réponse .

24
Konrad Rudolph

Cela dépend du niveau d'optimisation donné au compilateur. Prenons par exemple ces deux fonctions, sémantiquement identiques. L'un est le style C++ 11, l'autre style C.

void foo1 (void)
{
    int arr[100];
    std::generate(std::begin(arr), std::end(arr), [](){return std::Rand()%100;});
}

void foo2 (void)
{
    int arr[100];
    for (int *i = arr; i < arr+100; i++) *i = std::Rand()%100;
}

Compiler ceci avec gcc -O4 émet un code qui est extrêmement similaire (pas identique, mais complexité équivalente) pour les deux fonctions.

Cependant, le lambda n'est pas aligné lors de la compilation non optimisé (et les appels std :: begin et std :: end non plus).

Ainsi, bien que le compilateur puisse (et fait) un excellent travail pour optimiser le code de style moderne lorsque cela lui est demandé, il y aura peut-être ou probablement une pénalité de performance pour ce type de code dans une version de débogage non optimisée.

12
pconnell