我们正在诊断应用程序服务器上的 Ruby 性能问题,并将其简化为一个简单的测试用例。
我们将开发集群中的机器与生产数据中心中的机器的性能进行比较。
我们使用了下面这个简单的 Ruby 单行程序:
5000000.times { a = []; a << 1; a.length }
我们对其进行了基准测试,结果显示其在生产机器上的速度始终慢了 55%。
显然可能存在的情况以及我们认为不可能存在的情况:
- 不同的软件 - 开发和生产机器从相同的 ubuntu 操作系统、ubuntu 安装脚本、软件包存储库安装,并且我们使用 puppet 来保持配置一致。
- 不同的硬件 - 可能,但请参见下文。
- 不同的负载——开发机器和生产机器都没有显著的负载,再次参见下文。
我们为什么不认为这是负载或者硬件的问题呢?
首先,它们的负载和硬件配置都差不多。其次,我们编写了一个python测试脚本:
n = 10000000
while n > 1:
n = n - 1
a = []
a.append(4)
len(a)
并且这个数字始终是 10%快点生产速度比开发速度快,这也是我们所期望的。如果问题出在负载或硬件上,Python 在生产速度上不是也会更慢吗?
简而言之,两台机器都使用 ESXi 进行虚拟化
开发虚拟机具有 4GB RAM,并托管在具有双四核 AMD Opteron 2376 @ 2.294Ghz 32GB 的机器上,为虚拟机提供一个虚拟核心
生产虚拟机具有 4GB RAM,并托管在具有双四核 AMD Opteron 2354 @ 2.211Ghz 32GB 的机器上,为虚拟机提供四个虚拟核心(更新:我们现在已在所有虚拟机上尝试使用一个虚拟核心,但没有任何区别)
操作系统是 Ubuntu Hardy 64位。我们的 Ruby 解释器是:
ruby 1.8.6 (2008-08-11 patchlevel 287) [x86_64-linux]
我们的 Python 解释器是
Python 2.5.2 (r252:60911, Jul 31 2008, 17:31:22)
注意:我们也尝试过使用 Ruby Enterprise Edition,结果是一样的。
答案1
我不知道发生了什么,但我想分享一些基于处理器的其他硬件差异,看看是否可以帮助其他人更接近答案。
- Opteron 2354 具有 2MB L-3 缓存,而 2376 具有 6MB。
- 2354 使用 PC2-5300 DDR2 RAM,而 2376 使用 PC2-6400 DDR2 RAM。
我对硬件不太熟悉,但我认为这意味着内存访问在开发机器上通常要快得多?那么,如果 Ruby 在某种程度上更加“占用大量内存”(我真的不知道我的意思!),那么它是否会表现为更大的性能差异?
(我曾寻找过新处理器中是否有一些虚拟化功能可以解释这种差异,但却一无所获。)
几个问题...
- 每台服务器上的其他虚拟机在做什么?
- 您是否看到另一个(不是基于 MRI 的)Ruby、可能是 JRuby 有同样的行为?
- 您可以使用 ltrace/strace 运行示例,看看时间都花在了哪里?请参见调试 Ruby:理解并排除 VM 和应用程序故障更多细节。
答案2
您是否尝试过减少提供给生产虚拟机的 vCPU 数量?
我知道这听起来适得其反,但您可能已经意识到了这一点,如果分配给任何给定 VM 的所有 vCPU 插槽都不空闲,ESX 将不会为 VM 提供任何 CPU 时间 - 即,如果分配给 VM 的 4 个 vCPU 并非全部空闲,则 VM 根本无法获得任何时间。我知道这听起来很疯狂,但请认真尝试降至 2 个或 1 个 vCPU 并再次运行它。
祝你好运。
答案3
实际上这也是我考虑的办法。由于生产服务器上的缓存较小,因此会更快耗尽缓存,不得不使用主内存。如果生产中的访问速度比开发中慢,则这可能是问题所在。
但是,同样的问题也会发生在 Python 上。由于 Ruby 和 Python 都是用 C 实现的,因此整数大小可能相同(不过这个假设可能是错误的)。
总之我还在寻找!
答案4
上周我在我们的系统上遇到了类似的问题。原来是因为有人安装/要求使用 activesupport,它修改了我们类堆栈中的一堆内容,从而导致速度变慢。只有在实际流量下运行时才会检测到这个问题。检查慢速系统上安装的 gem,并将版本与开发机器进行比较。可能有些地方出了问题。