104 votes

Implémentation lambda C ++11 et modèle de mémoire

J'aimerais avoir quelques informations sur la façon de penser correctement sur le C++11 fermetures et std::function en termes de la façon dont ils sont mis en œuvre et le fonctionnement de la mémoire est gérée.

Bien que je ne crois pas que dans l'est de l'optimisation, j'ai l'habitude de considérer soigneusement l'impact sur les performances de mon choix lors de l'écriture de ce nouveau code. Je fais aussi une bonne quantité de programmation en temps réel, par exemple sur les microcontrôleurs et pour les systèmes audio, les cas de non-déterministe de l'allocation/libération de mémoire pauses sont à éviter.

C'est pourquoi j'aimerais développer une meilleure compréhension de quand utiliser ou ne pas utiliser C++ lambdas.

Ma compréhension est qu'un lambda sans capturé fermeture est exactement comme un C de rappel. Toutefois, lorsque l'environnement est capturé par valeur ou par référence, un objet anonyme est créé sur la pile. Lorsqu'une valeur de fermeture doit être retourné à partir d'une fonction, d'une enveloppe en std::function. Ce qui se passe à la fermeture de la mémoire dans ce cas? Est-il copié à partir de la pile, le tas? Est-il libéré à chaque fois que l' std::function est libéré, c'est à dire, est-il référence compté comme un std::shared_ptr?

J'imagine que, dans un système temps-réel je pourrais mettre en place une chaîne de lambda fonctions, passage de B comme une continuation de l'argument à Un, de sorte qu'un pipeline de traitement A->B est créé. Dans ce cas, A et B des fermetures seraient attribués une fois. Bien que je ne suis pas sûr de savoir si celles-ci seraient alloués sur la pile ou le tas. Cependant, en général, ce qui semble sûr à utiliser dans un système en temps réel. D'autre part, si B constructions certains lambda fonction de C, dont il retourne, alors que la mémoire C seraient attribués et libéré à plusieurs reprises, ce qui ne serait pas acceptable pour l'utilisation en temps réel.

En pseudo-code, un DSP boucle, ce qui, je pense, d'être en temps réel de sécurité. Je veux effectuer un traitement de bloc A, puis B, où les appels de son argument. Ces deux fonctions renvoient std::function objets, de sorte que f sera std::function objet, lorsque son environnement est stockée sur le tas:

auto f = A(B);  // A returns a function which calls B
                // Memory for the function returned by A is on the heap?
                // Note that A and B may maintain a state
                // via mutable value-closure!
for (t=0; t<1000; t++) {
    y = f(t)
}

Et qui je pense peut être mauvais pour l'utilisation en temps réel de code:

for (t=0; t<1000; t++) {
    y = A(B)(t);
}

Et un autre où je pense que la pile de la mémoire est probablement utilisé pour la fermeture:

freq = 220;
A = 2;
for (t=0; t<1000; t++) {
    y = [=](int t){ return sin(t*freq)*A; }
}

Dans ce dernier cas, la fermeture est construit à chaque itération de la boucle, mais contrairement à l'exemple précédent, il est bon marché parce que c'est comme un appel de fonction, pas de tas allocations sont faites. Par ailleurs, je me demande si un compilateur pourrait "l'ascenseur" de la fermeture et de faire un alignement des optimisations.

Est-ce correct? Je vous remercie.

110voto

Nicol Bolas Points 133791

Ma compréhension est qu'un lambda sans capturé fermeture est exactement comme un C de rappel. Toutefois, lorsque l'environnement est capturé par valeur ou par référence, un objet anonyme est créé sur la pile.

Non; c'est toujours un objet C++ avec un type inconnu, créé sur la pile. Une capture moins lambda peut être converti en un pointeur de fonction (si si il est adapté pour les conventions d'appel C est dépendant de l'implémentation), mais cela ne signifie pas qu'il est un pointeur de fonction.

Lorsqu'une valeur de fermeture doit être retourné à partir d'une fonction, on l'enveloppe dans std::function. Ce qui se passe à la fermeture de la mémoire dans ce cas?

Un lambda n'est rien de spécial en C++11. C'est un objet comme n'importe quel autre objet. Une lambda expression des résultats dans un fichier temporaire, qui peut être utilisé pour initialiser une variable sur la pile:

auto lamb = []() {return 5;};

lamb est une pile d'objets. Il a un constructeur et un destructeur. Et il suivra toutes les C++ règles pour que. Le type d' lamb contiendra les valeurs de références qui sont capturés; ils seront membres de l'objet, comme tous les autres membres de l'objet de tout autre type.

Vous pouvez le donner à un std::function:

auto func_lamb = std::function<int()>(lamb);

Dans ce cas, il pourra obtenir une copie de la valeur de lamb. Si lamb avaient capturé quelque chose en valeur, il y aurait deux copies de ces valeurs, l'une en lamb, et une en func_lamb.

Lorsque le champ d'application actuel se termine, func_lamb sera détruit, suivie par lamb, selon les règles de nettoyage de la pile des variables.

Vous pouvez tout aussi facilement allouer sur le tas:

auto func_lamb_ptr = new std::function<int()>(lamb);

Exactement là où la mémoire pour le contenu d'un std::function passe est dépendant de l'implémentation, mais le type effacement de salariés en std::function nécessite généralement au moins une allocation de mémoire. C'est pourquoi, std::functions'constructeur peut prendre un allocateur.

Il est libéré lorsque le std::function est libéré, c'est à dire, est-il référence compté comme un std::shared_ptr?

std::function stocke une copie de son contenu. Comme presque tous de la bibliothèque standard C++, function utilise la valeur sémantique. Ainsi, il est copiable; lorsqu'il est copié, le nouveau function objet est complètement séparé. Il est également mobile, donc internes, les allocations peuvent être transférées de manière appropriée sans avoir besoin de l'allocation et de la copie.

Ainsi, il n'est pas nécessaire pour le comptage de référence.

Tout le reste vous l'état est correct, en supposant que "l'allocation de la mémoire" équivaut à "mal de l'utiliser en temps réel de code".

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X