Le nombre de processeurs affecte-t-il les performances des threads asynchrones dans une application Web ASP.NET MVC 4?

StackOverflow https://stackoverflow.com/questions/8390034

Question

Je ne suis pas très doué pour la programmation asynchrone, donc la question se situe peut-être à un niveau bas.

J'ai créé une méthode async comme ci-dessous avec Async CTP sur ASP.NET MVC 4 Dev.Aperçu:

public class Movie
{
    public string Title { get; set; }
    public string Url { get; set; }
    public string BoxArtUrl { get; set; }
}

public class MovieM {

    public IEnumerable<Movie> M2009 { get; set; }
    public IEnumerable<Movie> M2010 { get; set; }
    public IEnumerable<Movie> M2011 { get; set; }
}

public class HomeController : AsyncController  {

    public async Task<ActionResult> GetMoviesM()  {

        var profiler = MiniProfiler.Current; // it's ok if this is null

        var pageSize = 1000;
        var imageCount = 0;

        using (profiler.Step("Start pulling data (Async) and return it")) { 

            var m2009 = await QueryMoviesAsync(2009, imageCount, pageSize);
            var m2010 = await QueryMoviesAsync(2010, imageCount, pageSize);
            var m2011 = await QueryMoviesAsync(2011, imageCount, pageSize);

            return View(new MovieM { 
                M2009 = m2009,
                M2010 = m2010,
                M2011 = m2011
            });
        }
    }

    XNamespace xa = "http://www.w3.org/2005/Atom";
    XNamespace xd = "http://schemas.microsoft.com/ado/2007/08/dataservices";
    XNamespace xm = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata";

    string query = "http://odata.netflix.com/Catalog/Titles?$filter=ReleaseYear eq {0}&$skip={1}&$top={2}&$select=Url,BoxArt";

    async Task<IEnumerable<Movie>> QueryMoviesAsync(int year, int first, int count)  {

        var client = new WebClient();
        var url = String.Format(query, year, first, count);
        var data = await client.DownloadStringTaskAsync(new Uri(url));
        var movies =
            from entry in XDocument.Parse(data).Descendants(xa + "entry")
            let properties = entry.Element(xm + "properties")
            select new Movie
            {
                Title = (string)entry.Element(xa + "title"),
                Url = (string)properties.Element(xd + "Url"),
                BoxArtUrl = (string)properties.Element(xd + "BoxArt").Element(xd + "LargeUrl")
            };
        return movies.AsEnumerable();
    }
}

Le code fonctionne très bien.Lorsque nous exécutons la même fonction sur une application de bureau (une application WPF par exemple), nous pouvons constater une différence de performances tangible.L'interface utilisateur n'est pas bloquée, les données sont instantanément envoyées à l'écran lorsqu'elles sont disponibles.

Mais sur une application Web, je ne vois vraiment pas de différence.J'ai également créé la même fonction que la synchronisation et les deux sont presque les mêmes.

Ce que j'aimerais savoir, c'est que:

  1. J'exécute cette application sur une machine équipée d'un processeur Intel Core 2 Duo T5750 2,00 GHz .Le nombre de processeurs affecte-t-il les performances du thread asynchrone sur C #?
  2. Est-ce que je fais quelque chose de mal ici du point de vue d'une application Web?
Était-ce utile?

La solution

Lorsque nous exécutons la même fonction sur une application de bureau (une application WPF par exemple), nous pouvons constater une différence de performances tangible. L'interface utilisateur n'est pas bloquée, les données sont instantanément envoyées à l'écran lorsqu'elles sont disponibles.

C'est une idée fausse courante. La méthode que vous avez publiée est légèrement plus lente que son équivalent synchrone, mais dans une application d'interface utilisateur, elle est plus réactive , et par conséquent apparaît plus performante.

Dans un scénario ASP.NET, la page n'est rendue que lorsque toutes les requêtes asynchrones sont terminées; c'est pourquoi vous ne voyez pas de différence.

Vous pouvez le rendre réellement plus performant en parallélisant vos requêtes:

var m2009Task = QueryMoviesAsync(2009, imageCount, pageSize);
var m2010Task = QueryMoviesAsync(2010, imageCount, pageSize);
var m2011Task = QueryMoviesAsync(2011, imageCount, pageSize);
await Task.WhenAll(m2009Task, m2010Task, m2011Task);
var m2009 = await m2009Task;
var m2010 = await m2010Task;
var m2011 = await m2011Task;

Cela améliorera les performances pour le bureau et ASP.NET.

Votre code d'origine utilise une composition en série (un code générique à la fois). Dans ce cas, le code s'exécute de manière asynchrone, mais la demande n'est pas terminée plus rapidement. Il y a toujours un avantage à utiliser await / async comme ceci: la demande ne lie pas un thread ASP.NET, votre service peut donc évoluer davantage. Dans le monde de l'interface utilisateur, le thread de l'interface utilisateur n'est pas lié, il est donc plus réactif. Mais pour ASP.NET et l'interface utilisateur, le temps total nécessaire pour terminer la méthode await n'est pas réduit en la rendant GetMoviesM.

La composition parallèle (en utilisant async ou Task.WhenAll) permet à Task.WhenAny dans son ensemble de s'exécuter plus rapidement, car il effectue les requêtes en parallèle. De plus, vous bénéficiez des avantages des fils mentionnés ci-dessus.

Dans ce cas, le nombre de processeurs n'a pas d'importance. Ils n'entrent en jeu que lorsque vous effectuez un traitement sur le pool de threads (par exemple, GetMoviesM), pas lorsque vous effectuez des E / S. (C'est une simplification, mais c'est vrai).

Autres conseils

Quelques commentaires.

Premièrement, WebClient n'ouvre par défaut que 2 connexions par serveur et par session.Cela aura évidemment un impact sur votre capacité de mise à l'échelle, vous voudrez peut-être changer cela [voir Comment puis-je supprimer par programmation la limite de 2 connexions dans WebClient ]

Deuxièmement, je ne suis pas sûr qu'il y ait un avantage à utiliser async à la fois dans votre méthode de contrôleur ET dans la méthode QueryMoviesAsync.

Troisièmement, WebClient implémente IDisposable, vous devriez donc l'utiliser avec une instruction using (..).Ne pas le faire peut également affecter l'évolutivité.

Compte tenu de tous les changements ci-dessus, et pour répondre à votre question initiale, oui, le code doit évoluer sur plusieurs processeurs / cœurs au moment de l'exécution car c'est la valeur par défaut pour ASP.NET/IIS

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top