【问题标题】:Parallel.ForEach and conditional with normal loop in .NET.NET 中的 Parallel.ForEach 和有条件的正常循环
【发布时间】:2021-03-12 02:40:44
【问题描述】:

以下内容是否会在 5 时对 500 个项目进行并行处理,但会在 1 个项目上进行正常循环(没有性能影响)。我不希望在一次执行 1 项时使用 Parallel(如果有的话)对性能造成影响。

 Parallel.ForEach(scheduledMatchups, new ParallelOptions
            {
                MaxDegreeOfParallelism = scheduledMatchups.Count() > 500 ? 5 : 1
            }, 
  gridItem =>
            {
// Process
});

【问题讨论】:

  • 用例是什么,您是在寻找一般规则,还是有特定的代码片段需要优化? 正常负载是大于还是小于500?
  • 你有没有考虑把这个分支逻辑放到一个自定义的Partitioner中?
  • 我不知道Partitioner 会做什么。
  • @MikeFlynn 使用自定义Partitioner,您可以定义要从数据源创建多少块。 GetPartitions 返回一个 IList<IEnumerator<T>>,如果元素少于 500,则可以包含单个块,如果有更多项目,则可以包含 5 个块。 Reference

标签: .net multithreading performance task-parallel-library parallel.foreach


【解决方案1】:

您可以重构动作和循环序列以具有两个不同的执行路径,同时仍然只定义 动作 执行一次:

Action<GridItems> action = (gridItem) =>
{
    // Process
};

if (scheduledMatchups.Count < 500)
{
    scheduledMatchups.ForEach(action);
    // or for more control, you could iterate yourself
    // with a for or foreach and call the action() manually.
    //
    // foreach(var gridItem in scheduledMatchups) action(gridItem);
}
else
{
    Parallel.ForEach(scheduledMatchups, new ParallelOptions
    {
        MaxDegreeOfParallelism = 5
    }, action);
}

This answer on SO 提供了一些关于Parallel.ForEach 内部发生的其他情况的见解,如果您想要最终控制性能和实现,那么这个简单的分支逻辑仍然只以可重用的方式定义一次操作。

虽然从技术上讲存在开销,但如果您通常期望满足并行条件,那么开销通常不足以证明这种类型的工作只是为了它。

正如@Keith 在下面的 cmets 中指出的那样,Parallel.ForEach 不会,也不是旨在为“迭代次数有限的小循环体”提供性能改进。 This is covered in this article on concurrency and parallel programming.

如果你的 action 足够轻,那么即使有很多次迭代,你最好不要并行处理。


我发现使用Parallel.ForEach 通常是一个更好的练习,只有当你可以证明你正在执行的操作类型是正确的。如果操作本身需要一些可观察到的时间来处理,那么对于有限数量的操作,开销很可能是合理的。

与所有此类问题一样,您的情况的真正答案将始终取决于比此处实际记录更多的因素,因此您应该尝试这两种代码样式,并自行确定是否会产生影响方式是明显的或可接受的。

如果您以一种或另一种方式观察问题,请发布有关具体观察的信息。在选择是否并行时,您应该谨慎过度过早对最终使您的代码更难阅读或维护但对实际运行时间。

【讨论】:

  • 来自 MS 文档中的 anti patterns 部分“Small Loop Body with Few Iterations 如果您对只有有限数量数据的非常小的循环体使用并行循环,您可能不会获得性能改进要处理的元素。在这种情况下,并行循环本身所需的开销将主导计算。简单地将每个顺序 for 循环更改为 Parallel.For 不一定会产生好的结果。"
  • 我当然不建议让所有东西都并行@KeithNicholas 我更多地认为如果你有时想要并行处理这个特定的列表,你不妨总是使用并行,如果 99% 的时间列表中只有有限数量的项目,则为 1% 制定特定的代码路径。
  • 我更多的是作为信息添加,我完全是为了分析和发现实际问题,然后在需要时使用并行解决
  • 谢谢@KeithNicholas 听起来我们在同一个页面上,我第一次假设太多,重新编写它以明确这一点。
  • 问题是当我一次只处理超过 5 个项目时,比如说 900 个项目,我的计算机上的 CPU 达到 99%,这不是我想要的。如果我有多个客户同时这样做,那就是个问题。
猜你喜欢
  • 2021-05-21
  • 2011-09-30
  • 1970-01-01
  • 2015-11-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多