[英]Why my code does not speed up with a multithreaded Parallel.For loop?
我尝试使用System.Threading.Tasks
库将简单的顺序循环转换为并行计算循环。 代码会编译,返回正确的结果,但是它不会节省任何计算成本,否则会花费更长的时间。
编辑:抱歉,我可能已经简化了这个问题,并且在这样做时犯了一些错误。 为了附加更多信息,我在i7-4700QM上运行代码,并且在Grasshopper脚本中对其进行了引用。 这是实际的代码。 我也切换到非线程局部变量
public static class LineNet
{
public static List<Ray> SolveCpu(List<Speaker> sources, List<Receiver> targets, List<Panel> surfaces)
{
ConcurrentBag<Ray> rays = new ConcurrentBag<Ray>();
for (int i = 0; i < sources.Count; i++)
{
Parallel.For(
0,
targets.Count,
j =>
{
Line path = new Line(sources[i].Position, targets[j].Position);
Ray ray = new Ray(path, i, j);
if (Utils.CheckObstacles(ray,surfaces))
{
rays.Add(ray);
}
}
);
}
}
}
Grasshopper实现只收集sources
targets
和surfaces
,调用Solve
方法并返回rays
。 我知道将工作负载分配给线程很昂贵,但是它是如此昂贵吗? 还是ConcurrentBag
仅阻止并行计算?
另外,我的类是不可变的(?),但是如果我使用通用的List
则内核会中止该操作并引发异常,有人可以说出原因吗?
如果没有可靠,可靠地重现此问题的最佳,完整和可验证的代码示例 ,就不可能提供确定的答案。 您发布的代码甚至似乎都不是真实代码的摘录,因为声明为该方法的返回类型的类型与return
语句实际返回的值不同。
但是,当然,您发布的代码似乎不太适合使用Parallel.For()
。 您的Line
构造函数要证明并行化创建项的任务是相当昂贵的。 需要明确的是,这是唯一可能的胜利。
最后,您仍然需要将创建的所有Line
实例聚合到一个列表中,因此为Parallel.For()
任务创建的所有这些中间列表仅是纯开销。 而且聚合必须是序列化的(即,一次只能有一个线程可以将一个项目添加到result
集合中),并且以最坏的方式(每个线程只能在放弃锁之前添加一个项目,而另一个线程却只能添加一个项目)有机会)。
坦白地说,最好将每个本地List<T>
存储在一个集合中,然后在Parallel.For()
返回之后一次将它们全部聚集在主线程中。 那并不是使代码的性能比直接非并行实现更好。 但是至少它不太可能变得更糟。 :)
最重要的是,您似乎没有可以从并行化中受益的工作量。 否则,您需要以更清晰,更详细的方式解释该思想的基础。
如果我使用通用列表,则内核会中止操作并引发异常,有人可以说出原因吗?
您已经在使用List<T>
作为它的每个任务的本地数据,并且确实可以,因为任务不共享其本地数据。
但是,如果您问为什么尝试使用List<T>
而不是ConcurrentBag<T>
作为result
变量,为什么会得到异常,这完全是可以预期的。 List<T>
类不是线程安全的,但是Parallel.For()
将允许其运行的每个任务与所有其他任务同时执行localFinally
委托。 因此,您有多个线程都试图同时修改同一个非线程安全的集合。 这是灾难的秘诀。 幸运的是您得到了例外。 实际的行为是不确定的,很可能您只是破坏数据结构而导致运行时异常。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.