每个核心的最佳线程数

Posted

技术标签:

【中文标题】每个核心的最佳线程数【英文标题】:Optimal number of threads per core 【发布时间】:2010-12-15 15:56:19 【问题描述】:

假设我有一个 4 核 CPU,我想在最短的时间内运行一些进程。理想情况下,该过程是可并行化的,因此我可以在无限数量的线程上运行它的一部分,并且每个线程花费相同的时间。

由于我有 4 个内核,我不希望通过运行比内核更多的线程来提高任何速度,因为单个内核只能在给定时刻运行单个线程。我对硬件了解不多,所以这只是一个猜测。

在多于内核的线程上运行可并行化进程是否有好处?换句话说,如果我使用 4000 个线程而不是 4 个线程运行我的进程,它会更快、更慢还是在大约相同的时间内完成?

【问题讨论】:

我非常感谢你的问题,但我不明白你的第一个假设与你的问题有什么关系?即这句话:“每个线程花费相同的时间。” 【参考方案1】:

如果您的线程不执行 I/O、同步等操作,并且没有其他任何东西在运行,那么每个内核 1 个线程将为您带来最佳性能。然而,很可能并非如此。添加更多线程通常会有所帮助,但在某个时间点之后,它们会导致性能下降。

不久前,我在一台 2 个四核机器上进行性能测试,该机器在 Mono 上运行 ASP.NET 应用程序,负载相当不错。我们使用了最小和最大线程数,最后我们发现对于特定配置中的特定应用程序,最佳吞吐量介于 36 到 40 个线程之间。这些边界之外的任何东西都表现得更差。学过的知识?如果我是你,我会使用不同数量的线程进行测试,直到你找到适合你的应用程序的数量。

有一点是肯定的:4k 线程需要更长的时间。这是很多上下文切换。

【讨论】:

我认为 Gonzalo 的回答很好。我只想补充一点,您应该进行实验和测量。您的程序将不同于他、我的或其他任何人的程序,只有对您自己程序行为的测量才能正确回答您的问题。并行(或并发)程序的性能并不是一个可以仅从第一原则得出良好结论的领域。 +1, +answer:让我感到惊讶的是,拥有比核心更多的线程会带来更好的性能,尽管与竞争线程相比,如果更多的线程意味着更大的时间份额,这也是有道理的。如果我的应用程序能够检测到性能差异并自动将自身调整到最佳线程数,那就太好了。 在真实场景中应该不会让您感到惊讶。线程阻塞等待 IO 资源,如磁盘访问、网络等。还等待非 IO 资源(如其他线程)完成使用共享变量。您真正想要实现的是最少线程数,以便每个内核至少有一个线程可以始终运行。 每个核心 1 个线程不是最佳的。它需要稍微多一点,最好是两倍,因为如果一个线程被暂时阻塞,这将允许另一个线程运行。哪怕只是在记忆中。如果您拥有具有 SMT/HT 功能的系统(P4、I7、Sun Rock 等),这一点更为重要) 因此在我的回答中“情况很可能并非如此”。找到正确的数字取决于应用程序及其运行的架构。【参考方案2】:

我同意@Gonzalo 的回答。我有一个不进行 I/O 的进程,这是我发现的:

请注意,所有线程都在一个数组上工作,但范围不同(两个线程不会访问相同的索引),因此如果它们在不同的数组上工作,结果可能会有所不同。

1.86 的机器是带有 SSD 的 macbook air。另一台 Mac 是带有普通 HDD 的 iMac(我认为它是 7200 rpm)。 windows机器也有一个7200转的硬盘。

在本次测试中,最佳数量等于机器中的核心数。

【讨论】:

图表+1。显然,每个内核 1 个线程是最好的,但有趣的是,四核系统似乎不像其他系统那样具有更高的线程数(无论如何 -1 表示图表!通过整数值 x 坐标平滑曲线?从 1 2 3 到 10 20 30 到 50 100 的疯狂跳跃?并且 y 坐标是 10 的倍数加上 2 以进行良好测量。这是 Excel 做的,不是吗? @Spacedman 是的。恕我直言,平滑的曲线看起来更好看。 :D @PascalvKooten,问题不在于它看起来很漂亮,它乍一看是骗人的。首先,y 轴从 42 开始,夸大了测试机器之间的明显差异。其次,x 轴值的奇怪进展表明“所用时间”与“线程数”不成线性比例,蓝线尤其如此。我认为其他人(包括我自己)的问题在于它歪曲了数据。 @Spacedman 图表上的批评是我在过去 24 小时内遇到的最荒谬的事情。该图有帮助。很多。时期。可以做得更好吗?没人在乎。平滑曲线而不是离散?那是你的问题???我假设,你们所有人都不会在他们的答案中包含这样的图表,因为你没有额外的时间/精力让它看起来不错。这就是我的观点。【参考方案3】:

我知道这个问题已经很老了,但是自 2009 年以来事情已经发生了变化。

现在需要考虑两件事:内核数量,以及每个内核中可以运行的线程数量。

对于英特尔处理器,线程数由超线程定义,只有 2 个(如果可用)。但是超线程将您的执行时间缩短了两倍,即使不使用 2 个线程也是如此! (即两个进程之间共享 1 个管道——当你有更多进程时这很好,否则就不好了。更多的核心肯定更好!)

在其他处理器上,您可能有 2、4 甚至 8 个线程。因此,如果您有 8 个内核,每个内核支持 8 个线程,那么您可以有 64 个进程并行运行而无需上下文切换。

如果您使用标准操作系统运行,那么“无上下文切换”显然是不正确的,该操作系统将为您无法控制的各种其他事情进行上下文切换。但这是主要思想。某些操作系统允许您分配处理器,因此只有您的应用程序才能访问/使用所述处理器!

根据我自己的经验,如果你有很多 I/O,多线程是好的。如果您有非常繁重的内存密集型工作(读取源 1、读取源 2、快速计算、写入),那么拥有更多线程将无济于事。同样,这取决于您同时读取/写入的数据量(即,如果您使用 SSE 4.2 并读取 256 位值,这会停止所有线程的步骤......换句话说,1 个线程可能更容易实现和如果不是实际上更快,可能几乎一样快。这将取决于您的进程和内存架构,一些高级服务器为单独的内核管理单独的内存范围,因此假设您的数据被正确归档,单独的线程会更快......这就是为什么,在某些情况下架构,4 个进程将比 1 个进程有 4 个线程运行得更快。)

【讨论】:

可能还有其他的,但我知道的是 IBM 的 POWER 处理器。他们的系统每个处理器有 4 或 8 个线程。现在他们可以加入更多的核心,所以他们为每个核心提供 2 个线程...... 这是旧的,但大多数英特尔 i5、i7 都有多线程 cpu,例如 i7 cpu 通常有 4 个内核,但有 8 个线程。 处理器没有线程。它们具有物理和逻辑核心。使用超线程,单个物理核心充当两个逻辑核心。我有一个技术人员坚持认为具有线程的处理器是真实存在的,所以我在处理器的白板上画了一张图片,其中有一个线程主轴伸出来。 @TechnikEmpire 看看这个intel.com/content/www/us/en/processors/core/…,也许你也可以联系intel并给他们画线。【参考方案4】:

答案取决于程序中使用的算法的复杂性。我想出了一种方法,通过对两个任意数量的线程“n”和“m”的处理时间 Tn 和 Tm 进行两次测量来计算最佳线程数。对于线性算法,最佳线程数为 N = sqrt ( (mn(Tm*(n-1) – Tn*(m-1)))/(nTn -mTm) ) 。

请阅读我关于计算各种算法的最佳数量的文章:pavelkazenin.wordpress.com

【讨论】:

为什么它被否决了?很抱歉,但这是对这个问题的最佳答案。 gonzalo 解决了问题的粗体部分,pkazen 解决了标题。两个答案都非常有用,但 pkazen 答案是相关的,因为我们有一个系统的方法来近似线程数。他甚至给出了线性算法的公式。 我没有投反对票,但如果我投了反对票,那是因为没有真正解释最佳线程数为何或如何与算法的复杂性相关,请保存通过阅读整个链接的文章,这是一个长篇阅读(因为文章的复杂性)。除此之外,我还不清楚文章的某些方面,最重要的是实验结果如何证实了该理论。 另外,我相信这个计算假设你有无限数量的 CPU 内核。虽然这绝对是有价值的信息,但问题是指具有少量内核的真实机器。【参考方案5】:

实际性能将取决于每个线程自愿让步的程度。例如,如果线程根本不执行 I/O 并且不使用系统服务(即它们是 100% cpu-bound),那么每个内核 1 个线程是最佳的。如果线程做任何需要等待的事情,那么您将不得不尝试确定最佳线程数。 4000 个线程会产生大量的调度开销,因此这也可能不是最优的。

【讨论】:

【参考方案6】:

我想我会在这里添加另一个视角。答案取决于问题是假设弱缩放还是强缩放。

来自Wikipedia:

弱扩展:对于每个处理器的固定问题大小,求解时间如何随处理器数量而变化。

强扩展性:对于固定的总问题大小,求解时间如何随处理器数量而变化。

如果问题假设缩放较弱,那么@Gonzalo 的回答就足够了。但是,如果问题是假设强大的扩展性,那么还有更多内容需要添加。在强扩展中,您假设工作负载大小是固定的,因此如果您增加线程数,每个线程需要处理的数据大小就会减少。在现代 CPU 上,内存访问成本很高,最好通过将数据保存在缓存中来保持局部性。因此,当每个线程的数据集适合每个内核的缓存时,可以找到可能的最佳线程数(我不详细讨论是否是 L1 /L2/L3 系统的缓存)。

即使线程数超过内核数也是如此。例如假设程序中有 8 个任意单元(或 AU)的工作将在 4 核机器上执行。

案例 1: 运行四个线程,每个线程需要完成 2AU。每个线程需要 10 秒才能完成(有很多缓存未命中)。四个核心的总时间为 10 秒(10 秒 * 4 线程 / 4 核心)。

案例 2: 运行 8 个线程,每个线程需要完成 1AU。每个线程只需 2 秒(而不是 5 秒,因为缓存未命中次数减少)。四核总时间为 4 秒(2 秒 * 8 线程 / 4 核)。

我已经简化了问题并忽略了其他答案中提到的开销(例如,上下文切换),但希望您明白拥有比可用内核数更多的线程数可能是有益的,具体取决于数据您正在处理的尺寸。

【讨论】:

【参考方案7】:

一次 4000 个线程是相当高的。

答案是肯定的和否定的。如果您在每个线程中执行大量阻塞 I/O,那么是的,您可以显示显着的加速,每个逻辑核心最多可能有 3 或 4 个线程。

如果你没有做很多阻塞的事情,那么线程的额外开销只会让它变慢。因此,使用分析器并查看每个可能平行的部分中的瓶颈在哪里。如果您正在进行大量计算,那么每个 CPU 超过 1 个线程将无济于事。如果您正在进行大量内存传输,那也无济于事。如果您正在执行大量 I/O,例如磁盘访问或 Internet 访问,那么是的,多线程将在一定程度上有所帮助,或者至少使应用程序更具响应性。

【讨论】:

【参考方案8】:

基准测试。

我会开始增加应用程序的线程数,从 1 开始,然后增加到 100 左右,为每个线程数运行 3-5 次试验,然后为自己构建一个运行速度与运行速度的图表。线程数。

您应该认为四线程情况是最佳的,之后运行时会略有上升,但也许不是。可能是您的应用程序受到带宽限制,即您加载到内存中的数据集很大,您会遇到很多缓存未命中等问题,因此 2 个线程是最佳的。

在你测试之前你无法知道。

【讨论】:

【参考方案9】:

通过运行返回机器上进程数的 htop 或 ps 命令,您将找到可以在机器上运行的线程数。

您可以使用有关“ps”命令的手册页。

man ps

如果要计算所有用户进程的数量,可以使用以下命令之一:

    ps -aux| wc -l ps -eLf | wc -l

计算用户进程数:

    ps --User root | wc -l

另外,你可以使用“htop” [Reference]:

在 Ubuntu 或 Debian 上安装:

sudo apt-get install htop

在 Redhat 或 CentOS 上安装:

yum install htop
dnf install htop      [On Fedora 22+ releases]

如果你想从源代码编译htop,你会发现它here。

【讨论】:

【参考方案10】:

理想的情况是每个核心 1 个线程,只要没有线程会阻塞。

这可能不正确的一种情况:内核上正在运行其他线程,在这种情况下,更多的线程可能会给您的程序带来更大的执行时间。

【讨论】:

这取决于您是否希望用户后台进程在您的应用程序运行时像垃圾一样运行。就此而言,您可以为每个线程设置一个实时优先级并获得最大的功率。但用户喜欢多任务处理。 好吧,我们正在处理一个神奇的理想并行应用程序。如果我曾经创造过这样的东西,我会觉得有权利随心所欲地占用 CPU。【参考方案11】:

大量线程(“线程池”)与每个内核一个的示例是在 Linux 或 Windows 中实现 Web 服务器。

由于在 Linux 中轮询套接字,许多线程可能会增加其中一个线程在正确的时间轮询正确套接字的可能性 - 但总体处理成本会非常高。

在 Windows 中,服务器将使用 I/O 完成端口 - IOCP - 实现应用程序事件驱动:如果 I/O 完成,操作系统会启动一个备用线程来处理它。当处理完成时(通常使用另一个 I/O 操作,如请求-响应对),线程返回到 IOCP 端口(队列)以等待下一次完成。

如果没有完成任何 I/O,则没有要完成的处理,也没有启动线程。

确实,Microsoft 建议在 IOCP 实现中每个内核不超过一个线程。任何 I/O 都可以附加到 IOCP 机制。如有必要,应用程序也可以发布 IOC。

【讨论】:

我不知道你在说哪个 Linux,但我的阻塞直到连接到达。我建议您阅读一些有关 select() 和 FD_SET() 以及类似函数/宏的内容。 好的,所以没有立即返回的异步表单? 来自 select() 手册页:timeout is an upper bound on the amount of time elapsed before select() returns. If both fields of the timeval structure are zero, then select() returns immediately. (This is useful for polling.) If timeout is NULL (no timeout), select() can block indefinitely.【参考方案12】:

从计算和内存绑定的角度(科学计算)来说,4000 个线程会使应用程序运行得非常慢。部分问题是上下文切换的开销非常高,而且很可能内存局部性很差。

但这也取决于您的架构。我从那里听说 Niagara 处理器应该能够使用某种先进的流水线技术在单个内核上处理多个线程。但是我没有使用这些处理器的经验。

【讨论】:

【参考方案13】:

希望这是有道理的,检查 CPU 和内存利用率并设置一些阈值。如果超过阈值,则不允许创建新线程,否则允许...

【讨论】:

以上是关于每个核心的最佳线程数的主要内容,如果未能解决你的问题,请参考以下文章

当我运行多个与 CPU 核心/线程数匹配的线程时,每个线程会在单独的核心/线程上运行吗?

线程池大小如何调?

CPU的核心数和线程数都啥意思

CUDA:每个线程计算的最佳像素数(灰度)

[C/C++11]_[初级]_[获取CPU支持的最合适的核心线程数]

[C/C++11]_[初级]_[获取CPU支持的最合适的核心线程数]