c# - 如何使用 Parallel.For/ForEach 获得最佳性能? (包括表演时间)

标签 c# .net multithreading performance parallel-processing

我正在尝试并行化我的 Web 解析工具,但速度提升似乎非常小。我有 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 中的“启动”时间要慢得多?

除此之外,并行循环的速度是标准 foreach 循环的 2.5 倍。这正常吗?

是否有我可以设置的设置,以便并行循环可以使用所有内核?

编辑:

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
}

最佳答案

HtmlAgilityPack.HtmlWeb 的简要阅读确认它正在使用同步 WebRequest API。因此,您将长时间运行的任务放入 ThreadPool(通过 Parallel)。 ThreadPool 专为短期操作而设计,可将线程快速返回到池中。阻塞 IO 是一个很大的禁忌。鉴于 ThreadPool 不愿启动新线程(因为它不是为这种用途而设计的),您将受到这种行为的限制。

异步获取您的 Web 内容(请参阅 herehere 以了解要使用的正确 API,您必须自己进一步调查...)这样您就不会束手无策具有阻塞任务的线程池。然后,您可以将解码后的响应提供给 HtmlAgilityPack 进行解析。

如果您真的想提高性能,您还需要考虑 WebRequest 无法执行异步 DNS 查找。在我看来,这是 WebRequest 设计中的一个可怕缺陷。

The BeginGetResponse method requires some synchronous setup tasks to complete (DNS resolution, proxy detection, and TCP socket connection, for example) before this method becomes asynchronous.

它使高性能下载成为真正的 PITA。大约在这个时候,您可能会考虑编写自己的 HTTP 库,以便所有内容都可以无阻塞地执行(从而使线程池处于饥饿状态)。

顺便说一句,在浏览网页时获得最大吞吐量是一件棘手的事情。根据我的经验,你得到了正确的代码,然后被它必须经过的路由设备所辜负。许多家用路由器根本无法胜任这项工作。

关于c# - 如何使用 Parallel.For/ForEach 获得最佳性能? (包括表演时间),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7734295/

相关文章:

c# - 获取客户端-服务器聊天以流式传输 C# 中的在线用户列表

c# - 使用 Rhino Mocks 模拟集合

c++ - 使用 QtConcurrent 加载 Pixmap 并绘制它

ruby-on-rails - Rails 查询执行导致数据库峰值

c# - 在 winforms 组合框中显示枚举值

c# - 根据另一个列表中的值拆分自定义列表

C# Linq 比 PHP 慢?解决谜题#236A

c# - C# 中的空 "if"语句是否会导致错误或警告?

c# - 多个类共享一个方法

java - 内部锁定、客户端锁定和外部锁定之间的区别?