Влияет ли количество процессоров на производительность асинхронных потоков в веб-приложении ASP.NET MVC 4? - PullRequest
3 голосов
/ 05 декабря 2011

Я не очень хорош в асинхронном программировании, поэтому вопрос может быть на низком уровне.

Я создал асинхронный метод, как показано ниже, с помощью Async CTP на ASP.NET MVC 4 Dev.Предварительный просмотр:

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

Код работает просто отлично.Когда мы запускаем ту же функцию в настольном приложении (например, в приложении WPF), мы видим ощутимую разницу в производительности.Пользовательский интерфейс не заблокирован, данные сразу выводятся на экран, когда они становятся доступными.

Но в веб-приложении я действительно не вижу разницы.Я также создал ту же функцию, что и синхронизация, и они почти одинаковы.

Я хотел бы знать, что:

  1. Я запускаю это приложение на компьютере, которыйимеет процессор Intel Core 2 Duo T5750 2,00 ГГц .Влияет ли количество процессоров на производительность асинхронного потока в C #?
  2. Я что-то не так делаю с точки зрения веб-приложения?

Ответы [ 2 ]

3 голосов
/ 07 декабря 2011

Когда мы запускаем ту же функцию в настольном приложении (например, в приложении WPF), мы видим ощутимую разницу в производительности.Пользовательский интерфейс не заблокирован, данные выводятся на экран немедленно, когда они становятся доступными.

Это распространенное заблуждение.Метод, который вы опубликовали, немного медленнее , чем его синхронный эквивалент, но в приложении с пользовательским интерфейсом он более отзывчив , и поэтому выглядит более производительным.

В сценарии ASP.NET страница отображается только после завершения всех асинхронных запросов;вот почему вы не видите разницы.

Вы можете сделать его на самом деле более производительным, распараллеливая ваши запросы:

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;

Это улучшит производительность длякак для настольных компьютеров, так и для ASP.NET.

В исходном коде используется последовательная композиция (по одному await за раз)В этом случае код выполняется асинхронно, но запрос не выполняется быстрее.Использование async / await по-прежнему имеет преимущество: запрос не связывает поток ASP.NET, поэтому ваша служба может расширяться.В мире пользовательского интерфейса поток пользовательского интерфейса не связан, поэтому он более отзывчив.Но как для ASP.NET, так и для пользовательского интерфейса метод итого время до завершения GetMoviesM метод не уменьшается, если сделать его async.

Параллельная композиция (использование Task.WhenAll или Task.WhenAny) позволяет GetMoviesM в целом работать быстрее, поскольку он выполняет запросы параллельно.Кроме того, вы получаете преимущества потоков, упомянутые выше.

В этом случае количество процессоров не имеет значения.Они вступают в игру только тогда, когда вы выполняете обработку в пуле потоков (например, Task.Run), а не при выполнении операций ввода-вывода.(Это упрощение, но достаточно верно).

0 голосов
/ 06 декабря 2011

Несколько комментариев.

Во-первых, WebClient по умолчанию открывает только 2 соединения на сервер за сеанс. Это, очевидно, повлияет на ваши возможности масштабирования, так что вы можете изменить это [см. Как программно удалить ограничение на 2 соединения в WebClient ]

Во-вторых, я не уверен, что есть какая-то польза от использования async как в вашем методе контроллера, так и в методе QueryMoviesAsync.

В-третьих, WebClient реализует IDisposable, поэтому вы должны использовать его с оператором using (..). Невыполнение этого требования также может повлиять на масштабируемость.

С учетом всех вышеперечисленных изменений и ответа на исходный вопрос, да, код должен масштабироваться на нескольких процессорах / ядрах во время выполнения, поскольку это значение по умолчанию для ASP.NET/IIS

.
...