¿El número de procesadores afectan el rendimiento de los hilos asíncronos en una aplicación web ASP.NET MVC 4?

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

Pregunta

No soy tan bueno en la programación asincrónica, por lo que la pregunta podría estar en el nivel bajo.

He creado un método Async como se muestra a continuación con Async CTP en ASP.NET MVC 4 Dev. Avance:

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();
    }
}

El código funciona bien. Cuando ejecutamos la misma función en una aplicación de escritorio (una aplicación WPF, por ejemplo), podemos ver una diferencia de rendimiento tangible. La interfaz de usuario no está bloqueada, los datos se están presionando para detectar instantáneamente cuando está disponible.

Pero en una aplicación web, realmente no puedo ver una diferencia. También creé la misma función que Sync y los dos son casi iguales.

Lo que me gustaría saber es que:

  1. Ejecuto esta aplicación en una máquina que tiene Intel Core 2 Duo CPU T5750 2.00GHz. ¿El número de procesadores afectan el rendimiento del hilo asincrónico en C#?
  2. ¿Estoy haciendo algo mal aquí desde el punto de vista de la aplicación web?
¿Fue útil?

Solución

Cuando ejecutamos la misma función en una aplicación de escritorio (una aplicación WPF, por ejemplo), podemos ver una diferencia de rendimiento tangible. La interfaz de usuario no está bloqueada, los datos se están presionando para detectar instantáneamente cuando está disponible.

Este es un concepto erróneo común. El método que publicaste es ligeramente Más lento que su equivalente síncrono, pero en una aplicación de interfaz de usuario es más sensible, y por lo tanto aparecer más actuante.

En un escenario ASP.NET, la página solo se representa cuando todas las solicitudes asíncronas se han completado; Por eso no ves una diferencia.

Tu puedes hacerlo Realmente Más desempeño paralelizando sus solicitudes:

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;

Esto mejorará el rendimiento tanto para el escritorio como para ASP.NET.

Su código original usa composición en serie (una await a la vez). En este caso, el código se ejecuta asíncronamente, pero la solicitud no se completa más rápido. Todavía hay un beneficio de usar async/await Así: la solicitud no une un hilo ASP.NET, por lo que su servicio puede ampliar más. En el mundo de la interfaz de usuario, el hilo de la interfaz de usuario no se ata, por lo que es más receptivo. Pero para ASP.NET y UI, el total hora de completo la GetMoviesM El método no se reduce al hacerlo async.

Composición paralela (usando Task.WhenAll o Task.WhenAny) permite GetMoviesM en su conjunto para funcionar más rápido, ya que hace las solicitudes en paralelo. Además, obtienes los beneficios del hilo mencionados anteriormente.

En este caso, el número de procesadores no importa. Solo entran en juego cuando estás procesando en el grupo de hilos (por ejemplo, Task.Run), no cuando se realiza I/O. (Esta es una simplificación, pero lo suficientemente cierto).

Otros consejos

Algunos comentarios.

En primer lugar, WebClient de forma predeterminada solo abre 2 conexiones por servidor, por sesión. Esto obviamente afectará su capacidad de escala, por lo que es posible que desee cambiar eso [ver ¿Cómo puedo eliminar programáticamente el límite de conexión 2 en WebClient? ]

En segundo lugar, no estoy seguro de que haya ningún beneficio al usar Async tanto en su método de controlador como dentro del método QueryMoviesAsync.

En tercer lugar, WebClient implementa Idisposable, por lo que debe usarlo con una declaración Using (..). No hacerlo también puede afectar la escalabilidad.

Dados todos los cambios anteriores y para responder a su pregunta original, sí, el código debe escalar a través de varios procesadores/núcleos en tiempo de ejecución, ya que ese es el valor predeterminado para ASP.NET/IIS

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top