如何为超线程/多核选择最佳线程数?

Posted

技术标签:

【中文标题】如何为超线程/多核选择最佳线程数?【英文标题】:How do I pick the best number of threads for hyptherthreading/multicore? 【发布时间】:2010-11-01 19:09:10 【问题描述】:

我在 .NET 3.5 控制台应用程序中有一些令人尴尬的并行工作,我想利用超线程和多核处理器。 我如何选择最佳数量的工作线程,以便在任意系统上充分利用其中任何一个? 例如,如果它是双核,我将需要 2 个线程;四核我想要4个线程。我最终想要的是确定处理器特性,这样我就可以知道要创建多少线程。

我不是在问如何拆分工作,也不是在问如何进行线程化,而是在问如何确定将运行此控制台应用程序的任意机器上的“最佳”线程数。

【问题讨论】:

“最佳”数量也不一定是您拥有的处理器数量。它基于此,但取决于您在做什么,线程并不总是在处理,因此拥有比处理器更多的线程通常更有效。 【参考方案1】:

我建议您不要尝试自己确定。使用 ThreadPool 并让 .NET 为您管理线程。

【讨论】:

这对我一点帮助都没有。如果我在单核、非超线程系统上创建 4 个线程,那么我就是在浪费资源。如果我在四核上创建 2 个线程,那么我只使用了一半的处理器。 使用 ThreadPool 确实会带来开销,但它也将责任委托给以明智的方式处理调度的明智方。您可能能够比 GarbageCollector 更好地处理内存管理,因此您是否计划实现自己的垃圾收集器。我不会试图猜测 ThreadPool 是如何工作的。【参考方案2】:

唯一的办法就是根据性能数据结合数据和代码分析。

不同的 CPU 系列和速度、内存速度以及系统上的其他活动都会使调优有所不同。

可能会进行一些自我调整,但这意味着需要进行某种形式的现场表演调整和自我调整。

【讨论】:

【参考方案3】:

您可以使用Environment.ProcessorCount,如果这是您唯一需要的。但通常使用 ThreadPool 确实是更好的选择。

.NET 线程池还规定有时会分配 更多 个线程而不是内核,以在许多线程等待 I/O 完成的某些情况下最大化吞吐量。

【讨论】:

ProcessorCount 确实是我所追求的。关于 ThreadPool:就像我对 Mark 的评论,如果我不知道要创建多少个线程,ThreadPool 如何帮助我? ThreadPool 自行决定在给定系统上使用多少线程。除非您另有说明,否则它基本上使用相同的属性(如果我没记错的话)。 如果您知道您将立即发送工作,您可以使用 ThreadPool.SetMinThreads 来计算处理器数量。【参考方案4】:

可以说,选择最佳线程数的真正方法是让应用程序对其自身进行分析,并根据提供最佳性能的因素自适应地更改其线程行为。

【讨论】:

没想到那个!谢谢,我会为我的项目查看那个,因为它与运行它的机器无关,而是与远程 SqlServer 相关。【参考方案5】:

我最近读到了一些关于此的内容(例如,请参阅this question 的已接受答案)。

简单的答案是让操作系统来决定。它可以比你更好地决定什么是最佳的。

有许多关于类似主题的问题 - 搜索“最佳线程数”(不带引号)会给您几页结果。

【讨论】:

【参考方案6】:

我写了一个使用多线程的简单数字运算应用程序,发现在我的四核系统上,它使用 6 个线程在固定时间内完成了最多的工作。

我认为唯一真正的确定方法是通过试验或分析。

【讨论】:

【参考方案7】:

鉴于您完全受 CPU 限制,一个好的经验法则是 processorCount+1

这是 +1,因为您总是会启动/停止/中断一些任务,而 n 任务几乎永远不会完全填满 n 处理器。

【讨论】:

+1 因为您将启动/停止/中断一些任务,而 n 个任务几乎永远不会完全填满 n 个处理器。 请解释反对意见 - 我想知道我做错了什么。谢谢。 我想有一种理论认为 +1 可能会导致启动/停止/上下文切换。 我的建议来自javaconcurrencyinpractice.com——一本关于并发的好书。至于可能导致它的 +1 - 任务最终会完成,并且为了弥补准备新任务的时间,您需要使用由操作系统有效调度的额外线程。【参考方案8】:

我想说这也取决于您在做什么,如果您制作服务器应用程序,那么通过Environment.ProcessorCount 或线程池使用 CPU 的所有资源是个好主意。 但是,如果它在桌面或不专门用于此任务的机器上运行,您可能希望让一些 CPU 空闲,以便机器为用户“运行”。

【讨论】:

这是一个由用户运行的控制台应用程序,所以我想通过使用所有“处理器”来减少执行时间。 ProcessorCount 确实是我想要的。【参考方案9】:

最佳数量就是处理器数量。理想情况下,您总是在 CPU(逻辑或物理)上运行一个线程,以最大限度地减少上下文切换和随之而来的开销。

这是否是正确的数字取决于(正如每个人所说的那样)您正在做什么。线程池(如果我理解正确的话)几乎尝试使用尽可能少的线程,但每次线程阻塞时都会启动另一个线程。

阻塞从来都不是最优的,但如果你正在做任何形式的阻塞,那么答案就会发生巨大的变化。

获得良好(不一定是最佳)行为的最简单和最简单的方法是使用线程池。在我看来,它真的很难比线程池做得更好,所以这只是最好的起点,只有在你能证明为什么这还不够好时才考虑其他事情。

【讨论】:

【参考方案10】:

甚至比 ThreadPool 更好,使用来自 TPL 的 .NET 4.0 Task 实例。任务并行库建立在 .NET 4.0 框架的基础之上,该框架实际上将确定最佳线程数,以尽可能高效地执行任务。

【讨论】:

【参考方案11】:

正确的数字显然是42。

现在是认真的。永远只使用线程池。

1) 如果您有一个冗长的处理任务(即 CPU 密集型)可以划分为多个工件餐,那么您应该划分您的任务,然后将所有单独的工作项提交给 @ 987654322@。线程池将获取工作项并以动态方式开始处理它们,因为它具有自我监控功能,包括根据需要启动新线程并且可以是configured at deployment by administrators according to the deployment site requirements,而不是在开发时预先计算数字。虽然正确的处理任务的分区大小可以考虑可用 CPU 的数量,但正确的答案在很大程度上取决于任务的性质和在这个阶段甚至不值得讨论的数据(此外主要关注点应该是您的NUMA nodes、内存局部性和互锁缓存争用,然后才是核心数)。

2) 如果您正在执行 I/O(包括 DB 调用),那么您应该使用异步 I/O 并在称为完成例程的 ThreadPool 中完成调用。

这两个是您应该拥有多个线程的唯一正当理由,并且最好使用 ThreadPool 来处理它们。其他任何事情,包括为每个“请求”或“连接”启动一个线程实际上都是 Win32 API 世界的反模式(fork 在 *nix 中是一个有效的模式,但在 Windows 上绝对不是)。

对于更专业和方式更详细的主题讨论我只能推荐 Rick Vicik 关于该主题的论文:

designing-applications-for-high-performance-part-1.aspx designing-applications-for-high-performance-part-ii.aspx designing-applications-for-high-performance-part-iii.aspx

【讨论】:

【参考方案12】:

除了处理器计数之外,您可能还需要通过计算 GetProcessAffinityMask 函数返回的关联掩码中的位来考虑进程的处理器关联。

【讨论】:

【参考方案13】:

如果线程运行时没有过多的 i/o 处理或系统调用,那么线程数(主线程除外)一般等于您系统中的处理器/内核数,否则您可以尝试通过测试增加线程数。

【讨论】:

以上是关于如何为超线程/多核选择最佳线程数?的主要内容,如果未能解决你的问题,请参考以下文章

请问多核CPU还需要超线程技术吗?还有多核CPU的带宽怎样计算?

[转]物理CPUCPU核数逻辑CPU超线程

CPU工作方式多核心超线程技术详解[转贴]

怎么根据任务管理器看是处理器是几核几线程的?

单核CPU多线程多核CPU和多个CPU

多线程和CPU的关系