Venkat Subramaniam 博士在《Programming Concurrency on the JVM》中提到关于最优线程数的计算:
- The minimum number of threads is equal to the number of available cores.
- If all tasks are computation intensive, then this is all we need.
- Having more threads will actually hurt in this case because cores
- would be context switching between threads when there is still work to do.
- If tasks are IO intensive, then we should have more threads.
- We can compute the total number of threads we’d need as follows:
-
- Number of threads = Number of Available Cores / (1 - Blocking Coefficient)
-
- To determine the number of threads, we need to know two things:
- • The number of available cores
- • The blocking coefficient of tasks
根据描述,最佳线程数量=CPU执行核心数/(1-阻塞系数);
许多程序员就立马写个程序测试,模拟一个阻塞系数0.5的线程,硬件配置CPU执行核心数是4,恰好2倍核心数,最佳线程数量是8,然后就开8个线程去处理。测试完成任务处理时间是316ms。以为这样是最佳了。然后又抱着怀疑的态度再做一轮测试,特意开16个线程去一跑,发现时间只用了215ms。更少了。咦?怎么回事呢?然后得出结论:最佳线程数量并非最佳。 4倍才是最佳的。是这样吗?问题出在哪里了呢?
其实,如果全系统只运行你一个程序,只考虑测试代码,阻塞系数是0.5,当然是没错的,但是这个公式是针对全系统而言的,需要把全系统所有程序的线程占用的timeslice都计算在内考虑。理论计算是需要考虑所有细节的,这些细节占用的时间可一点不少。
当全系统线程数量远远大于执行核心数,则增加了线程的上下文环境交换时间等等,还有其他线程分走了许多CPU时间片,这部分时间可不少,可是却没有用在你的测试计算上。为简化理解,直接把这部分时间都划算到阻塞时间上,等于说阻塞系数变大,纯工作系数变小;分母就变小了,最佳线程数量就成倍增大了。
如果还不理解,我举个例子,用实际场景来解释就明白了。
一个加工厂有4条加工流水线,每一个人同时只能占用一条流水线加工自己的产品,假定一个人占据一条加工流水线完全饱和工作8小时能完成自己的加工,那么拆分到4条线上显然2小时可以加工完成,理论上。
再假定人的饱和工作时间和空闲时间各占50%呢(构造阻塞系数0.5),在不考虑交班交接的时间,显然8个人同时排队加工才能在2小时完成,(加工厂只安排你的8个人)。
但是事实工厂工况是怎么样的呢?这个加工厂有1000人在排队等着加工自己的产品(你安排了8个人过去加工你的产品 + 992个外人),而且,一条流水线线上工作内容切换时,前一个人和后一个人交替交接需要占用不少时间,而且,992个外人也各自要加工自己的产品,分配给你那8个人的时间片机会就少了。那些时间都没为你的产品加工,都直接是划归到阻塞时间上去,阻塞系数变大,最佳线程数=CPU执行核心数/(1-阻塞系数), 分母变小,结果自然变大。如果你要获得更多的时间片,你就要安排更多人过去,增大占时间片的机会。
所以你安排8个人去加工,肯定2小时完成不了,可能实际用了3小时才加工完成。但是你若安排16个人过去排队加工,可能2.5小时就能加工完成了。
如此而已。如有疑问,站内私信消息或留言。