#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
?