Comment faire stackless coroutines diffèrent de stackful coroutines?
De fond:
Je vous pose cette question car j'ai actuellement une application avec plusieurs (centaines de milliers) de threads. La plupart de ces threads sont en veille une grande partie du temps, de l'attente sur les éléments de travail pour être placé dans une file d'attente. Lorsqu'un élément de travail est disponible, il est ensuite traité par l'appel de certains arbitrairement complexe de code existant. Sur certaines configurations de système d'exploitation, l'application se heurte à des paramètres au noyau régissant le nombre maximum de processus utilisateur, donc j'aimerais expérimenter avec des moyens de réduire le nombre de threads de travail.
Ma solution proposée:
Il semble comme une coroutine approche fondée sur les, où j'ai remplacer chaque thread de travail avec une coroutine, serait de les aider à accomplir cela. Je peux alors avoir une file d'attente de travail soutenu par un pool de réels (noyau) de threads de travail. Lorsqu'un élément est placé dans une coroutine de la file d'attente pour le traitement, l'entrée serait placé dans le pool de threads de la file d'attente. Il serait alors reprendre le correspondant de la coroutine, traiter ses données en file d'attente, puis de le suspendre de nouveau, libérant ainsi le thread de travail à faire d'autres travaux.
Détails de mise en œuvre:
Dans la réflexion sur comment je pourrais faire cela, je vais avoir de la difficulté à comprendre les différences fonctionnelles entre les stackless et stackful coroutines. J'ai une certaine expérience à l'aide de stackful coroutines à l'aide de la Coup de pouce.Coroutine de la bibliothèque. Je trouve que c'est relativement facile à comprendre à partir d'un niveau conceptuel: pour chaque coroutine, il conserve une copie de la CPU contexte et de la pile, et lorsque vous passez à une coroutine, il passe à celui enregistré contexte (tout comme un mode noyau planificateur serait).
Ce qui est moins clair pour moi, c'est comment un stackless coroutine diffère de ce. Dans mon application, le montant des frais généraux associés à la mise en attente des éléments de travail est très important. La plupart des implémentations que j'ai vu, comme la nouvelle bibliothèque de CO2 suggèrent que stackless coroutines fournir beaucoup plus faible surcharge des changements de contexte.
Donc, j'aimerais comprendre les différences fonctionnelles entre les stackless et stackful coroutines plus clairement. Plus précisément, je pense à ces questions:
-
Références comme celui-ci suggèrent que la distinction réside dans où vous pouvez rendement/reprendre dans un stackful vs stackless coroutine. Est-ce le cas? Est-il un simple exemple de quelque chose que je peux faire dans un stackful coroutine, mais pas dans un stackless un?
-
Il y a des restrictions sur l'utilisation de stockage automatique des variables (c'est à dire les variables sur la pile")?
-
Il y a des restrictions sur ce que les fonctions que j'pouvez appeler à partir d'un stackless coroutine?
-
Si il n'y a pas de sauvegarde de contexte de la pile pour un stackless coroutine, où le stockage automatique des variables d'aller quand la coroutine est en cours d'exécution?
- "La plupart de ces threads sont en veille une grande partie du temps, de l'attente sur les éléments de travail pour être placé dans une file d'attente" - si c'est le cas, pourquoi il y a autant de fils?
- Pour des raisons d'héritage. Je ne dis pas que c'est une bonne conception comme l'est, d'où mon désir de l'améliorer. Refactoring de l'ensemble de l'application en gros n'est pas à court terme de l'option, donc je suis à la recherche d'relativement simple rénovation pour commencer. Potentiellement compliquer encore plus les choses, le blocage d'appels de la file d'attente est généralement composé de plusieurs niveaux de profondeur dans la pile d'appel (c'est à dire pas dans le thread de travail est fonction de niveau supérieur). Je pense que cela permettrait d'éviter l'utilisation de stackless threads dans ce contexte spécifique.
Vous devez vous connecter pour publier un commentaire.
Tout d'abord, merci de prendre un coup d'oeil à CO2 🙂
Le Coup De Pouce.Coroutine doc décrit l'avantage de stackful coroutine bien:
Ce que cela signifie pour vous? par exemple, imaginez que vous avez une fonction qui prend un visiteur:
Vous voulez le transformer en itérateur, avec stackful coroutine, vous pouvez:
Mais avec stackless coroutine, il n'y a aucun moyen de le faire:
En général, stackful coroutine est plus puissant que stackless coroutine.
Alors, pourquoi voulons-nous stackless coroutine? réponse courte: l'efficacité.
Stackful coroutine généralement besoin d'allouer une certaine quantité de mémoire pour accueillir son exécution-pile (il doit être assez grand), et le contexte-switch est plus cher par rapport à la stackless, par ex. le coup de pouce.Coroutine prend 40 cycles de CO2 prend seulement 7 cycles en moyenne sur ma machine, car la seule chose qu'un stackless coroutine besoins de la restauration est le compteur de programme.
Cela dit, avec le soutien de la langue, probablement stackful coroutine pouvez également prendre l'avantage de l'compilateur calculée max-taille de la pile tant qu'il n'y a pas de récursivité dans la coroutine, de sorte que l'utilisation de la mémoire peut également être améliorée.
Parlant de stackless coroutine, garder à l'esprit que cela ne signifie pas qu'il n'y a pas d'exécution de la pile à tous, cela signifie seulement qu'il utilise le même moteur d'exécution-pile de l'hôte, de sorte que vous pouvez appeler des fonctions récursives en tant que bien, juste que tous les récurrences qui va se passer sur l'hôte de l'exécution de la pile. En revanche, avec stackful coroutine, lorsque vous appelez une fonction récursive, les récurrences qui va se passer sur la coroutine est propre pile.
Pour répondre aux questions:
(c'est à dire les variables sur la pile")?
Pas. C'est l'émulation de la limitation des émissions de CO2. Avec le soutien de la langue, le stockage automatique des variables visible à la coroutine sera mis sur la coroutine de stockage interne. Remarque j'ai mis l'accent sur "visible à la coroutine", si la coroutine appelle une fonction qui utilise le stockage automatique des variables en interne, alors ces variables sera mis sur l'exécution de la pile. Plus précisément, stackless coroutine n'a qu'à préserver les variables/temporaires qui peuvent être utilisés après la reprise.
Pour être clair, vous pouvez utiliser le stockage automatique des variables de CO2 de la coroutine corps ainsi:
Aussi longtemps que la définition ne fait pas précéder toute
await
.stackless coroutine?
Pas.
où puis-stockage automatique des variables d'aller quand la coroutine est
en cours d'exécution?
Répondu ci-dessus, un stackless coroutine n'est pas pour le stockage automatique des variables utilisées dans les fonctions dites, ils vont juste être placé sur la normale à l'exécution de la pile.
Si vous avez le moindre doute, il suffit de cocher CO2 du code source, il peut vous aider à comprendre la mécanique sous le capot 😉
Ce que vous voulez d'utilisation des terres fils ou fibres - habituellement, vous souhaitez suspendre votre code (course en fibres) dans un profond imbriquée pile d'appel (par exemple, l'analyse des messages de TCP-connexion). Dans ce cas, vous ne pouvez pas utiliser stackless la commutation de contexte (la pile d'application est partagée entre stackless coroutines -> pile de cadres de sous-routines appelées serait écrasé).
Vous pouvez utiliser quelque chose comme coup de pouce.la fibre qui implémente l'utilisateur des terres fils ou fibres basé sur coup de pouce.contexte.
boost::fiber
à partir d'un autre thread du noyau qu'il ne l'était déjà suspendu? Est-il un gain de performance pour le faire? Que penser d'unboost::asymmetric_coroutine
?