Как получить максимальную производительность, используя Parallel.For/ForEach? (включая время выполнения)

#c# #.net #multithreading #performance #parallel-processing

#c# #.net #многопоточность #Производительность #параллельная обработка

Вопрос:

Я пытаюсь распараллелить свой инструмент веб-анализа, но прирост скорости кажется очень минимальным. У меня i7-2600K (8 ядер с гиперпоточностью).

Вот некоторый код, чтобы показать вам идею. Я только показываю Parallel.ForEach , но вы поняли идею:

 List<string> AllLinks = this.GetAllLinks();
ConcurrentDictionary<string, Topic> AllTopics = new ConcurrentDictionary<string, Topic> ( );

int count = 0;
Stopwatch sw = new Stopwatch ( );
sw.Start ( );

Parallel.ForEach ( AllLinks, currentLink =>
{
    Topic topic = this.ExtractTopicData ( currentLink );
    this.AllTopics.TryAdd ( currentLink, topic );

      count;

    if ( count > 50 )
    {
        Console.WriteLine ( sw.ElapsedMilliseconds );
        count = 0;
    }
} );
  

Я получаю эти тайминги:

 Standard foreach loop:
24582
59234
82800
117786
140315

2 links per second


Paralel.For:

21902
31649
41168
49817
59321


5 links per second

Paralel.ForEach:
10217
20401
39056
49220
58125

5 links per second
  

Во-первых, почему время «запуска» намного медленнее Parallel.For ?

Кроме этого, параллельные циклы дают мне скорость в 2,5 раза больше, чем стандартный цикл foreach. Это нормально?

Есть ли настройка, которую я могу установить, чтобы параллельные циклы могли использовать все ядра?

Редактировать:

Вот в значительной степени то, что ExtractTopicData делает:

 HtmlAgilityPack.HtmlWeb web = new HtmlWeb ( );
HtmlAgilityPack.HtmlDocument doc = web.Load ( url );
IEnumerable<HtmlNode> links = doc.DocumentNode.SelectNodes ( "//*[@id="topicDetails"]" );

var topic = new Topic();

foreach ( var link in links )
{
    //parse the link data
}
  

Комментарии:

1. Что именно это делает. Извлеките topicdata. Веб-запросы? Какой API?

2. Извините, забыл упомянуть, он использует пакет гибкости html и выполняет некоторый синтаксический анализ. Добавлены подробности выше.

3. Кроме того, вы изменяете локальную переменную (count) небезопасным образом. Это будет ненадежно.

4. @Judah, это исказит результаты синхронизации?

5. @JoanVenge Большая часть этого времени тратится на ввод-вывод. По сути, ваши потоки ожидают завершения загрузки. Это не будет потреблять много процессора.

Ответ №1:

Краткое прочтение HtmlAgilityPack.HtmlWeb подтверждает, что он использует синхронный WebRequest API. Поэтому вы помещаете длительные задачи в пул потоков (через Parallel ). Пул потоков предназначен для кратковременных операций, которые быстро возвращают поток обратно в пул. Блокировка ввода-вывода — это большой запрет. Учитывая нежелание пула потоков запускать новые потоки (поскольку он не предназначен для такого использования), вы будете ограничены таким поведением.

Извлекайте ваш веб-контент асинхронно (смотрите Здесь и здесь для правильного использования API, вам нужно будет продолжить расследование самостоятельно …), Чтобы вы не связывали пул потоков с блокирующими задачами. Затем вы можете отправить декодированный ответ в HtmlAgilityPack для синтаксического анализа.

Если вы действительно хотите повысить производительность, вам также необходимо учитывать, что WebRequest не способен выполнять асинхронный поиск DNS. IMO это ужасный недостаток в дизайне WebRequest.

Метод BeginGetResponse требует выполнения некоторых задач синхронной настройки (например, разрешение DNS, обнаружение прокси и подключение к сокету TCP), прежде чем этот метод станет асинхронным.

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

Кроме того, получение максимальной пропускной способности при просмотре веб-страниц — сложная задача. По моему опыту, вы получаете правильный код, а затем его подводит оборудование маршрутизации, через которое оно должно проходить. Многие отечественные маршрутизаторы просто не справляются с этой задачей.

Комментарии:

1. Спасибо, как я могу асинхронно извлекать свой веб-контент? У меня все мои ссылки предварительно сгенерированы. Это то, что вы имели в виду? Итак, в основном у меня есть 1000 ссылок, готовых к анализу, поэтому я могу использовать проанализированную информацию для дальнейшего получения нужных мне данных, таких как название темы, владелец темы, дата и т.д., Путем анализа данных, предоставленных my с помощью пакета html agility pack.

2. Кстати, я реализовал веб-ответ и передал его в html agility pack, но производительность не намного лучше. Начальные GetAllLinks в 2 раза быстрее, но другие остаются такими же и по-прежнему используют очень низкий процессор (3%). Наконец, вы знаете о DownloadStringAsync ? Должен ли я использовать это вместо DownloadString ?