为不同类型的任务使用不同的线程池是否值得开销?
Is using different thread pools for different types of tasks worth the overhead?
我正在设计一个 class 来提供有关 Collatz 序列组的统计信息。我的目标之一是能够以最大效率同时处理大量包含大量项(数百甚至数千位数字)的序列。
为此,我计划对每个单独的统计数据使用最佳数据收集技术,这意味着 ForkJoinPool
可以更有效地处理某些任务,而标准缓存和固定线程池可以更有效地处理其他任务Executors
中提供。创建多个线程池的开销,或者关闭一个线程池并创建另一个线程池的开销,如果我走那条路,成本会超过我节省的成本吗?
Would the overhead of creating multiple thread pools, or shutting one down and creating another, if I went that route, cost me more than I would save?
我们怎么可能告诉你这个?
关闭和重新启动线程池肯定会产生开销。有种的话。创建线程并不便宜。
但是,我们无法量化使用不同类型的线程池可以节省多少。如果我们无法量化,就无法就您的策略是否有效...向您提供建议。
(但我认为反复关闭并重新创建线程池不是一个好主意。idle 池对性能的影响很小。)
此 "smells" 过早优化。 (这就像在制造发动机缸体之前尝试调整赛车的发动机!)
我的建议是(主要是1)一开始就忘记性能。现在,专注于获得有用的东西。这是我会做的:
- 使用最简单的策略实现代码,编写测试用例,测试/调试直到它工作。
- 选择一个示例问题或一组您将要尝试解决的典型问题
- 实施测试工具,让您可以针对示例问题衡量代码的性能。 (注意 Java 基准测试的标准问题...)
- 对您的代码进行基准测试。
- 速度够快吗?现在停止。
- 如果没有,继续。
- 实施备选策略之一,并测试/调试。
- 对修改后的代码进行基准测试。
- 速度够快吗?现在停止。
- 看清楚没用吗。放弃它,尝试另一种策略。
- 你能调整一下吗?如果是这样,请尝试。
- 转到5。
此外,以这样一种方式实施不同的策略可能是值得的,您可以使用命令行或配置文件设置调整它们或在它们之间切换。
作为一般规则,很难先验确定任何复杂算法或策略的执行情况。一般来说,理论……或直觉……方法要考虑的因素太多,无法给出可靠的预测。基准测试和调优是必经之路。
1 - 显然,如果您 知道 某些技术或算法会表现不佳,并且您有一个更好的替代方案,其实现的努力大致相同...做明智的事。
由于您只谈论两种不同类型的池(fork-join 和 Executor
基于池),并且您声称至少您的某些任务更适合一种类型或池或另一方面,使用两种类型的池的开销很可能是值得的。
毕竟,您可以让两种类型的池都保持活动状态,因此设置池和创建线程只需要 一次 成本,而(显然) 这两种池类型的好处将适用于整个处理过程。由于您正在做 "enormous" 的工作量,即使是很小的收益最终也会加起来并超过一次性成本(这可能以每个线程的微体系结构来衡量)。
这一观察的关键在于,对于您未使用的池中现有但不活动的线程,没有真正的持续开销。
当然说了,简答吧"just try both approaches and measure it!"。
我正在设计一个 class 来提供有关 Collatz 序列组的统计信息。我的目标之一是能够以最大效率同时处理大量包含大量项(数百甚至数千位数字)的序列。
为此,我计划对每个单独的统计数据使用最佳数据收集技术,这意味着 ForkJoinPool
可以更有效地处理某些任务,而标准缓存和固定线程池可以更有效地处理其他任务Executors
中提供。创建多个线程池的开销,或者关闭一个线程池并创建另一个线程池的开销,如果我走那条路,成本会超过我节省的成本吗?
Would the overhead of creating multiple thread pools, or shutting one down and creating another, if I went that route, cost me more than I would save?
我们怎么可能告诉你这个?
关闭和重新启动线程池肯定会产生开销。有种的话。创建线程并不便宜。
但是,我们无法量化使用不同类型的线程池可以节省多少。如果我们无法量化,就无法就您的策略是否有效...向您提供建议。
(但我认为反复关闭并重新创建线程池不是一个好主意。idle 池对性能的影响很小。)
此 "smells" 过早优化。 (这就像在制造发动机缸体之前尝试调整赛车的发动机!)
我的建议是(主要是1)一开始就忘记性能。现在,专注于获得有用的东西。这是我会做的:
- 使用最简单的策略实现代码,编写测试用例,测试/调试直到它工作。
- 选择一个示例问题或一组您将要尝试解决的典型问题
- 实施测试工具,让您可以针对示例问题衡量代码的性能。 (注意 Java 基准测试的标准问题...)
- 对您的代码进行基准测试。
- 速度够快吗?现在停止。
- 如果没有,继续。
- 实施备选策略之一,并测试/调试。
- 对修改后的代码进行基准测试。
- 速度够快吗?现在停止。
- 看清楚没用吗。放弃它,尝试另一种策略。
- 你能调整一下吗?如果是这样,请尝试。
- 转到5。
此外,以这样一种方式实施不同的策略可能是值得的,您可以使用命令行或配置文件设置调整它们或在它们之间切换。
作为一般规则,很难先验确定任何复杂算法或策略的执行情况。一般来说,理论……或直觉……方法要考虑的因素太多,无法给出可靠的预测。基准测试和调优是必经之路。
1 - 显然,如果您 知道 某些技术或算法会表现不佳,并且您有一个更好的替代方案,其实现的努力大致相同...做明智的事。
由于您只谈论两种不同类型的池(fork-join 和 Executor
基于池),并且您声称至少您的某些任务更适合一种类型或池或另一方面,使用两种类型的池的开销很可能是值得的。
毕竟,您可以让两种类型的池都保持活动状态,因此设置池和创建线程只需要 一次 成本,而(显然) 这两种池类型的好处将适用于整个处理过程。由于您正在做 "enormous" 的工作量,即使是很小的收益最终也会加起来并超过一次性成本(这可能以每个线程的微体系结构来衡量)。
这一观察的关键在于,对于您未使用的池中现有但不活动的线程,没有真正的持续开销。
当然说了,简答吧"just try both approaches and measure it!"。