为什么禁用超线程会使我的服务器变慢?

我有一台主要运行Ruby脚本的服务器。由于Ruby(2.7)具有GIL,它是单线程的。 我的计算机(服务器)配备了Intel i3双核处理器,但由于超线程技术,我看到了4个核心。在高负载下,Ruby只利用25%的CPU。我想看看禁用超线程是否对单线程运行的编程语言有益。 此外,我的服务器运行着一个非常简化的桌面环境,并且不会使用超过2%的CPU。因此,我希望尽可能地利用资源供给Ruby使用。我进行了基准测试,以查看禁用超线程是否真的能提升性能。

基准测试:

我写了一个简单的 Ruby 脚本,它运行一个 while 循环,并将循环计数器的值与另一个变量相加。这个程序应该使用 CPU 核心的100%:

#!/usr/bin/env ruby
$-v = true

LOOPS = ENV['N'].to_i.then { |x| x < 1 ? 100_000_000 : x } + 1
i, j, t = 0, 0, Time.now

puts "Counting till #{LOOPS - 1} and adding values to V..."
while (i += 1) < LOOPS
    if i % 10000 == 0
        e = Time.now - t
        r = LOOPS.*(e)./(i).-(e).round(2)
        print "\e[2KN: #{i} | Done: #{i.*(100) / LOOPS}% | Elapsed: #{e.round(2)}s | Estimated Rem: #{r}s\r"
    end

    j += i
end

puts "\nV = #{j}\nTime: #{(Time.now).-(t).round(2)}s"
启用超线程技术后:
⮚ ruby p.rb
Counting till 100000000 and adding values to V...
N: 100000000 | Done: 99% | Elapsed: 4.55s | Estimated Rem: 0.0s
V = 5000000050000000
Time: 4.55s

⮚ ruby p.rb
Counting till 100000000 and adding values to V...
N: 100000000 | Done: 99% | Elapsed: 4.54s | Estimated Rem: 0.0s
V = 5000000050000000
Time: 4.54s

⮚ ruby p.rb
Counting till 100000000 and adding values to V...
N: 100000000 | Done: 99% | Elapsed: 4.67s | Estimated Rem: 0.0s
V = 5000000050000000
Time: 4.67s

gnome-system-monitor 在测试运行期间报告了 Ruby 占用 25% 的 CPU 使用率。

  • 没有超线程:

[ 使用 # echo 0 | tee /sys/devices/system/cpu/cpu{2,3}/online 命令来禁用超线程 ]

⮚ ruby p.rb
Counting till 100000000 and adding values to V...
N: 100000000 | Done: 99% | Elapsed: 4.72s | Estimated Rem: 0.0s
V = 5000000050000000
Time: 4.72s

⮚ ruby p.rb
Counting till 100000000 and adding values to V...
N: 100000000 | Done: 99% | Elapsed: 4.54s | Estimated Rem: 0.0s
V = 5000000050000000
Time: 4.54s

⮚ ruby p.rb
Counting till 100000000 and adding values to V...
N: 100000000 | Done: 99% | Elapsed: 4.56s | Estimated Rem: 0.0s
V = 5000000050000000
Time: 4.56s

gnome-system-monitor 在测试运行期间报告了 Ruby 占用 50% 的 CPU 使用率。


我甚至在我的笔记本电脑上运行了这个测试,所花费的时间大约是在我的电脑上的两倍。但结果是相同的:禁用超线程并不能帮助进程更好地运行。而且更糟的是,我的笔记本电脑在多任务处理时变得稍微慢了一些。 因此,在非超线程模式下,Ruby 使用了两倍的 CPU 功率与超线程模式相比。但为什么它完成相同的任务所需的时间仍然是相同的呢?

12echo 0 | tee /sys/devices/system/cpu/cpu{2,3}/online用于禁用超线程。” - 你确定这样正确地禁用了超线程吗?你可能只是将自己降为了一个带有超线程的核心,而不是两个没有超线程的核心。 - marcelm
根据我所阅读的关于双核处理器的资料,如果执行cat /sys/devices/system/cpu/cpuN/topology/core_id命令后输出0或1,那意味着它们在内部使用核心0和1。例如,在我的台式机和笔记本电脑上,都是双核i3处理器,执行cat /sys/devices/system/cpu/cpu{2,3}/topology/core_id命令分别输出0和1。而在我的树莓派3B上,输出结果为3和4,因为树莓派不支持超线程技术(在/proc/cpuinfo中也没有相关信息)。所以我认为对于我这个搭载i3双核处理器的系统来说,禁用3和4实际上就是禁用了超线程技术,对吗? - 15 Volts
4在这种情况下,是的,我希望这样能有效地禁用超线程(HT)。不过,如果你正在进行基准测试并且结果非常重要,我建议重新启动计算机并在BIOS/EFI中禁用超线程,以确保万无一失 :) - marcelm
好的,所以我已经在主板的UEFI设置中禁用了超线程支持,但性能并没有提升,反而在多任务处理时变得更糟。当我在启动后使用“echo 0 | tee ...”禁用时也有同样的效果。 - 15 Volts
15我看不出这两个测试之间有任何区别。在这两种情况下,数值都在4.5-4.7秒的范围内。 - Giacomo Alzetta
21你知道什么是超线程吗?现代的CPU大部分时间都在等待来自内存的数据。真的是很多时间。所以超线程的作用就是当一个线程在等待内存时,核心会将其放置一边并开始执行另一个线程。直到那个线程也需要等待内存。然后它们两个都会等待,直到其中一个获得数据并继续执行。这使得单个核心的使用更加高效,所以如果禁用超线程能让某些东西更快,我会感到非常惊讶。 - Vilx-
8@Vilx- 更简单的说:如果禁用超线程能提高速度,英特尔就不会在他们的芯片上加入超线程技术了。 - user253751
1@Vilx-,差异出奇地小 - 即使对于编译这种对内存依赖性最严重的情况,我发现一旦调度的线程数超过核心数,挂钟时间下降的速度不如 CPU 时间上升得快。我的最后一个实验是编译 KiCad,没有启用超线程时,CPU 时间为63分钟,启用超线程后为100分钟,挂钟时间从15.3分钟减少到12分钟。 - Simon Richter
@Vilx- 但这不是超线程的工作方式。至少不是Intel处理器上的那种。超线程是Intel对低级指令级并行性的一种廉价尝试,其中前端在两个线程之间交替获取指令(每个周期和停顿时)。后端只有通过两个架构状态才能意识到超线程的存在。因此,超线程的目标是充分利用所有微体系结构资源,与内存无关(顺便说一句,L1缓存命中需要4-5个周期),而与乱序执行有关。例如,一个CPU可以在2个执行单元中执行整数加法,但程序可能存在数据依赖性,强制加法操作必须串行执行。禁用超线程可以很容易地提高性能,但前提是... - Margaret Bloom
@Vilx- ... 你已经精心调整了(很可能是汇编)代码,以充分利用所有微架构资源。在这种情况下,另一个线程正在占用执行单元。 - Margaret Bloom
1因为Ruby(2.7)有一个GIL,所以它是单线程的。虽然它是多线程的,但一次只能执行一个线程。Ruby线程在同时有效地执行多个长时间运行的任务方面非常有用。而且,如果代码必须等待输入、I/O或网络,它们可以防止整个应用程序被阻塞。 - Schwern
是的,我知道Ruby中的Thread和Fiber类,但在较低级别上,它们无法同时使用多个核心。不过,Ruby 3.0可能不会再有GIL了... - 15 Volts
2个回答

你的Ruby程序在禁用超线程时并没有使用2倍的CPU时间。相反,由于它最大化了两个总核心中的一个核心,gnome-system-monitor会报告利用率为50%。如果由于超线程,系统报告了四个总核心,那么其中一个核心将是25%。 禁用超线程确实导致了结果的更多变化,因为可用资源较少:最近的英特尔(或AMD)核心非常宽,因此额外的线程通常有助于提取10-20%的更高综合性能。如果在测试运行期间自动执行了一些后台进程,则没有超线程的系统更容易出现变异和较低的总吞吐量。

3值得注意的是,许多CPU监视器相对于单个核心进行读取,在这些监视器中,您经常会看到一个进程占用>100%的CPU,因为它是线程化的,并利用了超过一个完整核心的CPU时间。解释结果时,了解您的监视器读取的内容非常重要。 - David Spillett
1是的,这就是top和类似命令的工作原理。 - shodanshok
@DavidSpillett 在Windows系统中,这可能会有点令人困惑,特别是因为随着新版本的推出,算法也发生了改变。Windows 8/Server 2012的默认视图与你描述的完全一样(相对于单个核心),而Windows 7/Server 2008 R2的视图(以及后续版本的详细视图)则是相对于整个CPU进行读取的。 - gparyani

我想看看禁用超线程是否对单线程运行的编程语言有益。 我不知道减少核心数量如何提高性能,即使是对于单线程应用程序也是如此。当启用超线程时,您的CPU将以4个虚拟核心运行。使用尽可能多的CPU的单线程应用程序将使用可用CPU的25%。当您禁用超线程时,将核心数量降低到2个。现在,该单线程应用程序可以使用可用CPU的50%。 Ruby并没有使用2倍的CPU,而是在禁用超线程时,您只有1/2的可用CPU。如果您有一个装满1/4水的大杯子,并将其倒入变成1/2水满的小杯子中,您仍然有相同数量的水。 我甚至在我的笔记本电脑上运行了测试,所花费的时间大约是在我的计算机上的两倍。但结果是相同的:禁用超线程对进程没有帮助。更糟糕的是,我的笔记本电脑在多任务处理时变得稍慢。 是的,你正在削减大约一半的CPU功率。这也会使Ruby线程运行变慢。假设你有3个线程想要同时运行,除了你的Ruby线程。如果你将虚拟核心减少到2个,那么你的Ruby线程很可能会被暂停一段时间,让其他线程有一些时间来运行。

2禁用超线程将提高对CPU的独占访问的高优先级任务的性能,因为没有来自低优先级任务的干扰指令,这可以减少该单个任务的性能约40%。使用超线程时,“所有任务完成”所需的墙钟时间将更短,而“高优先级任务完成”所需的墙钟时间将更短无需超线程,除非您拥有比核心更多的高优先级任务(因为如果每个人都具有高优先级,则没有人具有)。 - Simon Richter
1@SimonRichter 我甚至亲眼见过在禁用4核i7 CPU上的超线程时,1..4作业并行编译速度加快的情况。 - Ruslan
1@Ruslan 公平地说,这是七年前的情况,并且是在Linux上。Linux在保持驱动程序质量方面并不是最好的记录 - 很可能你使用的内核根本不知道HT“虚拟”核心,并将它们视为同质的,从而导致对已经部分利用的核心进行过度调度。看到是否有任何变化会很有趣,但另一方面,英特尔不再推荐使用HT(尽管原因是出于安全而非性能考虑)。 - Luaan
1@Luaan 很不可能完全不知道 HT:我记得在配置内核时启用了 CONFIG_SCHED_SMT - Ruslan
我已经在没有超线程的情况下运行了Xonotic游戏,Xonotic可以同时使用多个核心。但是,我仍然获得了FPS的提升,甚至在没有GPU的情况下也能启用一些特效! - 15 Volts

  • 相关问题