Parallel.ForEach 挂起一个大循环
Parallel.ForEach hangs for a large loop
我有一个 for 循环的实现,我正在使用 TPL 进行并行化。我使用的是配备 4GB RAM 和 i3 Core 处理器的戴尔笔记本电脑。我有多个 parallel.foreach
,它们是使用 Parallel.invoke
调用的。该程序是 Enterprise Architect 的插件,用于在 EA 中创建模型图和对象。
代码是这样的:
Parallel.invoke(()=>parent1Creation(),()=>parent2Creation(),...);
其中每个父创建是 Parallel.foreach
:
Parallel.foreach(parents, (parent) => {
//create parent
//create children
for(child in parent.children) {
childecreation();
}
for(child2 in parent.children) {
childecreation();
}
//can be any type and number of children
}
我有一个问题,当我的循环大小增加时,即大约 1500-2000 次迭代,Enterprise Architect 停止工作。
这是否是因为我的笔记本电脑配置或我使用并行循环或企业架构师的方式导致的问题。
我该如何解决。
我不建议采用这种策略。 运行 很多 Parallel.ForEach 循环一次不一定会提高你的性能(参见 post 后面的警告),特别是如果每个 Parallel.ForEach 循环都处理一个大量的迭代。在某些时候,使用额外的线程将不再有利于您的性能,只会增加开销。
这里需要注意的是 Parallel.ForEach 通常可以很好(但不完美)为特定的 foreach 循环选择最佳线程数。没有明确保证特定 foreach 循环将使用多少线程(甚至 将 运行 并行),因此可以想象多个 Parallel.ForEach 循环实际上会提高你的表现。最好的检查方法是使用调试器查看在任何给定点实际使用了多少线程。如果不是您所期望的,您可以检查 Parallel.ForEach 循环中代码的实现(例如);此时您还可以采取其他步骤来尝试提高性能(例如,针对 IO 绑定和其他非 CPU 绑定操作的良好 async/await 实现,以便线程可以完成更多工作- 见下文)。
简单的例子:假设您有一个系统,其中有 4 个线程和 4 个内核,而 4 个线程是系统上唯一 运行ning 的东西。 (显然这永远不会发生)。从调度的角度来看,明智的做法是让每个核心分别处理一个线程。假设每个线程一直都很忙(即它永远不会等待),添加额外的线程如何提高您的性能?例如,如果您启动 运行ning,6 个线程,那么显然至少一个核心现在必须 运行 至少 2 个线程,这会增加额外的开销,但没有明显的好处。这里的简化(可能不正确)假设是您的任务是 100% CPU-bound 并且线程实际上 运行 在不同的内核上。如果其中一个假设不正确,那就是一个明显的改进机会。例如,如果线程花费大量时间等待 IO 绑定操作的结果,CPU 上的多个线程实际上可以提高性能。您还可以考虑 async/await 实现以提高性能。
关键是在某些时候添加额外的线程不会给你带来任何性能优势,只会增加开销(特别是如果所涉及的任务主要是 CPU-bound 而不是主要是 IO-bound,因为例子)。没有办法解决这个事实。
非CPU绑定操作(IO绑定任务,例如对服务器的调用),其中主要阻塞正在等待来自CPU/memory外部的结果的并行化不同.事实上,async/await 不一定会 创建新线程;它的主要行为之一是 return 控制有问题的方法的调用者,并 "try" 在可能的情况下在同一线程上做其他工作。
重复我最喜欢的比喻,假设你作为 10 个人的一员出去吃饭。当服务员过来点菜时,服务员要求点的第一个人还没准备好,但其他九个人已经准备好了。服务员正确的做法是,与其等第一个人准备好点餐,不如让其他 9 个人先点餐,然后让第一个人准备好后再点餐。他肯定 不会 带第二个服务员来等待一个人准备好;在这种情况下,第二个服务员实际上可能不会减少完成订单所花费的总时间。这基本上就是 async/await 试图完成的;例如,如果所有操作都在等待来自服务器的结果,理想情况下您可以在等待时做其他事情。
另一方面,再打个比方,绝对不是服务员自己做饭的情况。在那种情况下,增加更多的人(通过类比,线程)会真正加快速度。
进一步扩展这个类比,如果厨房只有一个四眼炉灶,那么在 运行炉子大小的限制。一旦你达到这个限制,更多的厨房工作人员实际上会放慢速度,因为他们只会互相妨碍,因为实际上一次可以烹饪的东西的数量是有硬性限制的。无论您的厨房工作人员有多大,您都不可能一次在炉子上烹饪超过 4 种食物。在这种情况下,您拥有的核心数量就像厨房的大小;一旦你达到某个点,增加更多的厨房员工(线程)会降低你的表现(而不是提高它)。
如果您使用 RDBMS 支持的模型,您最好对模型做一些 SQL 来快速完成事情,而不是使用 EA 的 API。
https://leanpub.com/InsideEA结构上有很多细节
例如使用 SQLServer,使用原始 INSERT 比遍历 EA 对象要快得多,更不用说使用 JOIN 快速获取数据了。
与使用 API.
相比,SQL 我的脚本具有接近 100 倍以上的性能
不确定 EA COM 对象能否按您希望的方式调用。如果是这样,模型更新仍然必须以某种顺序发生才能正确分配 Object_IDs。这可以解释为什么您会 运行 某种锁定限制。
我有一个 for 循环的实现,我正在使用 TPL 进行并行化。我使用的是配备 4GB RAM 和 i3 Core 处理器的戴尔笔记本电脑。我有多个 parallel.foreach
,它们是使用 Parallel.invoke
调用的。该程序是 Enterprise Architect 的插件,用于在 EA 中创建模型图和对象。
代码是这样的:
Parallel.invoke(()=>parent1Creation(),()=>parent2Creation(),...);
其中每个父创建是 Parallel.foreach
:
Parallel.foreach(parents, (parent) => {
//create parent
//create children
for(child in parent.children) {
childecreation();
}
for(child2 in parent.children) {
childecreation();
}
//can be any type and number of children
}
我有一个问题,当我的循环大小增加时,即大约 1500-2000 次迭代,Enterprise Architect 停止工作。
这是否是因为我的笔记本电脑配置或我使用并行循环或企业架构师的方式导致的问题。
我该如何解决。
我不建议采用这种策略。 运行 很多 Parallel.ForEach 循环一次不一定会提高你的性能(参见 post 后面的警告),特别是如果每个 Parallel.ForEach 循环都处理一个大量的迭代。在某些时候,使用额外的线程将不再有利于您的性能,只会增加开销。
这里需要注意的是 Parallel.ForEach 通常可以很好(但不完美)为特定的 foreach 循环选择最佳线程数。没有明确保证特定 foreach 循环将使用多少线程(甚至 将 运行 并行),因此可以想象多个 Parallel.ForEach 循环实际上会提高你的表现。最好的检查方法是使用调试器查看在任何给定点实际使用了多少线程。如果不是您所期望的,您可以检查 Parallel.ForEach 循环中代码的实现(例如);此时您还可以采取其他步骤来尝试提高性能(例如,针对 IO 绑定和其他非 CPU 绑定操作的良好 async/await 实现,以便线程可以完成更多工作- 见下文)。
简单的例子:假设您有一个系统,其中有 4 个线程和 4 个内核,而 4 个线程是系统上唯一 运行ning 的东西。 (显然这永远不会发生)。从调度的角度来看,明智的做法是让每个核心分别处理一个线程。假设每个线程一直都很忙(即它永远不会等待),添加额外的线程如何提高您的性能?例如,如果您启动 运行ning,6 个线程,那么显然至少一个核心现在必须 运行 至少 2 个线程,这会增加额外的开销,但没有明显的好处。这里的简化(可能不正确)假设是您的任务是 100% CPU-bound 并且线程实际上 运行 在不同的内核上。如果其中一个假设不正确,那就是一个明显的改进机会。例如,如果线程花费大量时间等待 IO 绑定操作的结果,CPU 上的多个线程实际上可以提高性能。您还可以考虑 async/await 实现以提高性能。
关键是在某些时候添加额外的线程不会给你带来任何性能优势,只会增加开销(特别是如果所涉及的任务主要是 CPU-bound 而不是主要是 IO-bound,因为例子)。没有办法解决这个事实。
非CPU绑定操作(IO绑定任务,例如对服务器的调用),其中主要阻塞正在等待来自CPU/memory外部的结果的并行化不同.事实上,async/await 不一定会 创建新线程;它的主要行为之一是 return 控制有问题的方法的调用者,并 "try" 在可能的情况下在同一线程上做其他工作。
重复我最喜欢的比喻,假设你作为 10 个人的一员出去吃饭。当服务员过来点菜时,服务员要求点的第一个人还没准备好,但其他九个人已经准备好了。服务员正确的做法是,与其等第一个人准备好点餐,不如让其他 9 个人先点餐,然后让第一个人准备好后再点餐。他肯定 不会 带第二个服务员来等待一个人准备好;在这种情况下,第二个服务员实际上可能不会减少完成订单所花费的总时间。这基本上就是 async/await 试图完成的;例如,如果所有操作都在等待来自服务器的结果,理想情况下您可以在等待时做其他事情。
另一方面,再打个比方,绝对不是服务员自己做饭的情况。在那种情况下,增加更多的人(通过类比,线程)会真正加快速度。
进一步扩展这个类比,如果厨房只有一个四眼炉灶,那么在 运行炉子大小的限制。一旦你达到这个限制,更多的厨房工作人员实际上会放慢速度,因为他们只会互相妨碍,因为实际上一次可以烹饪的东西的数量是有硬性限制的。无论您的厨房工作人员有多大,您都不可能一次在炉子上烹饪超过 4 种食物。在这种情况下,您拥有的核心数量就像厨房的大小;一旦你达到某个点,增加更多的厨房员工(线程)会降低你的表现(而不是提高它)。
如果您使用 RDBMS 支持的模型,您最好对模型做一些 SQL 来快速完成事情,而不是使用 EA 的 API。
https://leanpub.com/InsideEA结构上有很多细节
例如使用 SQLServer,使用原始 INSERT 比遍历 EA 对象要快得多,更不用说使用 JOIN 快速获取数据了。
与使用 API.
相比,SQL 我的脚本具有接近 100 倍以上的性能不确定 EA COM 对象能否按您希望的方式调用。如果是这样,模型更新仍然必须以某种顺序发生才能正确分配 Object_IDs。这可以解释为什么您会 运行 某种锁定限制。