Considérons cette méthode asynchrone très simple:
static async Task myMethodAsync()
{
await Task.Delay(500);
}
Lorsque je compile ceci avec VS2013 (pré-compilateur Roslyn), la machine à états générée est une structure.
private struct <myMethodAsync>d__0 : IAsyncStateMachine
{
...
void IAsyncStateMachine.MoveNext()
{
...
}
}
Lorsque je le compile avec VS2015 (Roslyn), le code généré est le suivant:
private sealed class <myMethodAsync>d__1 : IAsyncStateMachine
{
...
void IAsyncStateMachine.MoveNext()
{
...
}
}
Comme vous pouvez le voir, Roslyn génère une classe (et non une structure). Si je me souviens bien, les premières implémentations du support async / await dans l'ancien compilateur (CTP2012 je suppose) ont également généré des classes, puis elles ont été modifiées en struct pour des raisons de performances. (dans certains cas , vous pouvez éviter complètement la boxe et l' allocation des tas ...) (Voir ce )
Est-ce que quelqu'un sait pourquoi cela a été changé à nouveau à Roslyn? (Je n'ai aucun problème à ce sujet, je sais que ce changement est transparent et ne change le comportement d'aucun code, je suis juste curieux)
Éditer:
La réponse de @Damien_The_Unbeliever (et le code source :)) à mon humble avis explique tout. Le comportement décrit de Roslyn s'applique uniquement à la version de débogage (et cela est nécessaire en raison de la limitation CLR mentionnée dans le commentaire). Dans Release, il génère également une structure (avec tous les avantages de cela ..). Cela semble donc être une solution très intelligente pour prendre en charge à la fois Edit et Continue et de meilleures performances en production. Des choses intéressantes, merci à tous ceux qui ont participé!
la source
async
les méthodes ont presque toujours un vrai point asynchrone - unawait
qui donne le contrôle, ce qui exigerait de toute façon que la structure soit encadrée. Je crois que les structs soulageraient uniquement la pression de la mémoire pour lesasync
méthodes qui s'exécutaient de manière synchrone.Réponses:
Je n'avais aucune pré-connaissance à ce sujet, mais comme Roslyn est open-source ces jours-ci, nous pouvons parcourir le code pour une explication.
Et ici, à la ligne 60 d'AsyncRewriter , on trouve:
// The CLR doesn't support adding fields to structs, so in order to enable EnC in an async method we need to generate a class. var typeKind = compilationState.Compilation.Options.EnableEditAndContinue ? TypeKind.Class : TypeKind.Struct;
Ainsi, bien qu'il y ait un certain intérêt à utiliser
struct
s, le grand avantage de permettre à Edit and Continue de fonctionner avec desasync
méthodes a été évidemment choisi comme la meilleure option.la source
Il est difficile de donner une réponse définitive pour quelque chose comme ça (à moins qu'un membre de l'équipe du compilateur n'intervienne :)), mais il y a quelques points que vous pouvez considérer:
Le «bonus» de performance des structures est toujours un compromis. En gros, vous obtenez ce qui suit:
Qu'est-ce que cela signifie dans le cas d'attente? Eh bien, en fait ... rien. Il n'y a qu'une très courte période pendant laquelle la machine à états est sur la pile - rappelez-vous, fait
await
effectivement areturn
, donc la pile de méthodes meurt; la machine d'état doit être préservée quelque part, et ce «quelque part» est définitivement sur le tas. La durée de vie de la pile ne convient pas bien au code asynchrone :)En dehors de cela, la machine à états enfreint certaines bonnes directives pour définir les structures:
struct
s doit avoir une taille maximale de 16 octets - la machine à états contient deux pointeurs qui, à eux seuls, remplissent parfaitement la limite de 16 octets sur 64 bits. En dehors de cela, il y a l'État lui-même, donc il dépasse la «limite». Ce n'est pas un gros problème, car il est fort probable qu'il ne soit jamais passé que par référence, mais notez que cela ne correspond pas tout à fait au cas d'utilisation des structures - une structure qui est essentiellement un type de référence.struct
s devrait être immuable - eh bien, cela n'a probablement pas besoin de beaucoup de commentaires. C'est une machine d'état . Encore une fois, ce n'est pas un gros problème, car la structure est du code généré automatiquement et privé, mais ...struct
s devrait logiquement représenter une valeur unique. Ce n'est certainement pas le cas ici, mais cela découle déjà d'un état mutable en premier lieu.Et bien sûr, tout cela est dans un cas où il n'y a pas de fermeture. Lorsque vous avez des locals (ou des champs) qui traversent le
await
s, l'état est encore gonflé, ce qui limite l'utilité d'utiliser une structure.Compte tenu de tout cela, l'approche de classe est définitivement plus propre et je ne m'attendrais pas à une augmentation notable des performances en utilisant à la
struct
place un . Tous les objets impliqués ont la vie même, la seule façon d'améliorer les performances de la mémoire serait de faire tout leurstruct
s (magasin dans une mémoire tampon, par exemple) - ce qui est impossible dans le cas général, bien sûr. Et la plupart des cas où vous utiliseriezawait
en premier lieu (c'est-à-dire, certains travaux d'E / S asynchrones) impliquent déjà d'autres classes - par exemple, des tampons de données, des chaînes ... Il est plutôt improbable que vous fassiezawait
quelque chose qui renvoie simplement42
sans rien faire. allocations de tas.En fin de compte, je dirais que le seul endroit où vous verriez vraiment une réelle différence de performance serait les repères. Et optimiser pour les benchmarks est une idée idiote, c'est le moins qu'on puisse dire ...
la source