我不确定 serverfault 是否是提出此问题的正确地方,但我想知道如果您必须为 Java Web 应用程序选择新的 CPU 类型,您会做出什么选择:
a) 具有 32 个内核且时钟速度为 2.5 Ghz 的 CPU
或者
b)具有 8 个内核但时钟速度为 3.8 Ghz 的 CPU
考虑到每个 Web 应用程序的传入 HTTP 请求都由一个空闲的 Java 线程处理,选择 a) 可能更有意义,因为您可以同时处理四倍以上的 HTTP 请求。然而,另一方面,CPU b) 可以更快地完成单个 HTTP 请求的处理...
你怎么认为?
旁注:
- 它必须是一台物理机器,在这种情况下,虚拟机或云解决方案不是一种选择
- RAM 并不重要,服务器最终将拥有 512GB RAM
- 缓存:Java Web 应用程序具有广泛的缓存框架,因此选择实际上取决于 CPU。
答案1
太长不看;真正的答案可能是“更多 RAM”,但正如你问的问题一样,答案当然是视情况而定。不过,32 核 @2.5Ghz 几乎肯定会击败 8 核 @3.8Ghz - 核心数量是 4 倍,时钟速度是 1.5 倍。这可不是一场公平的较量。
您应该考虑的一些因素是交易响应时间、并发用户和应用程序架构。
交易响应时间 如果您的 Java 应用程序在几毫秒内响应大多数请求,那么拥有更多核心来处理更多并发请求可能是最佳选择。但是,如果您的应用程序主要处理运行时间较长、更复杂的事务,那么更快的核心可能会对您有所帮助。(也可能不是 - 见下文)
并发用户和请求 如果您的 Java 应用程序收到大量并发请求,那么更多内核可能会有所帮助。如果您没有那么多并发请求,那么您可能只需要为一堆额外的空闲内核付费。
应用程序架构 如果应用服务器将大部分事务时间花在等待 Web 服务、数据库、kafaka/mq 等的响应上,那么我提到的那些长时间运行的请求不会从更快的核心中受益太多。我见过很多应用程序,它们的事务时间为 20-30 秒,它们只将一小部分响应时间花在应用程序本身的处理上,其余时间则等待数据库和 Web 服务的响应。
您还必须确保应用程序的各个部分能够很好地结合在一起。让 32 或 64 个线程分别处理一个请求,然后排队等待 JDBC 池中的 10 个连接之一(也称为 Python 问题中的猪),这对您没有多大帮助。现在进行一些规划和设计将为您节省大量性能故障排除的时间。
最后一件事 - 您可能要比较哪些 CPU?我能找到的最便宜的 32 核 2.5 GHz CPU 至少比任何 8 核 3.8 Ghz CPU 贵 3 到 4 倍。
答案2
假设您的 Java 网络服务器配置适当,您应该选择更多核心。
仍然存在依赖项,例如信号量、并发访问,无论内核数量或速度如何,仍会有一些线程在等待。但最好由 CPU(内核)而不是 OS(多线程)来管理。
无论如何,32 核 @2.5Ghz 将处理更多线程,并且比 8 核 @3.8Ghz 更好。
此外,CPU 产生的热量取决于频率(以及其他因素),并且不是线性的。也就是说,3.8Ghz 产生的热量将比 3.8/2.5 x 产生更多热量(必须根据您的确切 CPU 类型/品牌进行确认...许多网站都提供详细信息)。
答案3
您告诉我们,一个请求大约需要 100-200 毫秒才能执行,并且大部分是处理时间(尽管很难区分实际的 CPU 执行和实际的内存访问),很少的 I/O、等待数据库等。
您必须对两个 CPU 上实际需要的时间进行基准测试,但我们假设它在较慢的 CPU(有 32 个内核)上需要 150 毫秒,而在较快的 CPU(只有 8 个内核)上需要 100 毫秒。
那么第一个 CPU 每秒最多可以处理 32/0.15 = 213 个请求。
第二个 CPU 每秒最多可以处理 8/0.1 = 80 个请求。
因此,最大的问题是:您预计每秒有多少个请求?如果您每秒的请求数远不及几十个,那么您就不需要第一个 CPU,而第二个 CPU 将为您提供更快的执行时间。如果您确实需要每秒超过 100 个请求,那么第一个 CPU 是有意义的(或者拥有多个服务器可能更有意义)。
请注意,这是非常非常粗略的估计。唯一可以确定的方法是用实际负载对每台服务器进行基准测试。如上所述,快速 CPU 或具有大量内核的 CPU 很快就会出现内存访问不足的情况。各种 CPU 缓存的大小以及每个请求的“工作集”在这里非常重要。这是考虑到真正受 CPU 限制的工作,没有系统调用、没有共享资源、没有 I/O...
答案4
初步说明
我赞同@PossiblyUsefulProbablyNot的绝对有用的答案。
tldr;真正的答案可能是“更多 RAM”
尤其是这一点。
警告
本质上来说,我不太像一个管理员。
也许我更像一个软件工程师。
测量无可替代
我们知道什么
因此,机器
- 将运行基于 Java 的后端应用程序(企业版?)
- 公开(无论如何,在某个相当大的范围内)暴露一个处理客户端请求的 HTTP API
- 可能附加了某种形式的数据库
- 否则被描述为不太受 I/O 限制
- 不依赖于第三方服务的可用性、延迟或吞吐量
楼主描绘的图景并不模糊。但与此同时,数据还远远不足以给出答案关于原发帖人的个人情况。
当然,32 个内核,2/3 时钟速度有可能在速度优势相对较小的情况下,性能优于 1/4 的内核。当然,产生的热量与 4GHz 阈值以上的时钟速度不成正比。当然,如果我必须盲目地把鸡蛋放在一个篮子里,我会在任何一天选择 32 个内核。
我们不知道的是
仍然太多了。
然而,除了这些简单的事实之外,我对任何更具体、更客观的答案的假设性尝试都持怀疑态度。 当且仅当这是可能的(并且你有充分的理由相信单位时间的操作数是一个合理的关注点),拿到你打算运行系统的硬件,端到端测量和测试。
一个明智的决定涉及相关和可信的数据。
OP写道: RAM 并不重要
在绝大多数情况下,记忆是瓶颈。
当然,OP主要询问CPU 核心与时钟速度因此记忆就显得偏离主题了。
不过,我不这么认为。在我看来,这个问题更有可能是基于错误的前提。现在,@OP,别误会我的意思,你的问题切题,措辞恰当,你的担忧显然是真实的。我只是不相信哪个 CPU 在你的用例中表现“更好”的答案与你完全无关。
内存为何对 CPU 如此重要
主存储器是极其缓慢。
从历史上看,与硬盘相比,我们倾向于将 RAM 视为“快速存储类型”。在这种比较的背景下,它仍然适用。然而,在最近几十年中,处理器速度的增长速度一直远快于 DRAM 的性能。随着时间的推移,这种发展导致了通常所说的“处理器-内存-差距”。
处理器和内存速度之间的差距(来源:卡洛斯·卡瓦略,米尼奥大学信息系)
获取缓存行从主内存到 CPU 寄存器大约需要 100 个时钟周期时间。在此期间,您的操作系统将报告 x86 架构的 4 个核心之一中的两个硬件线程之一忙碌的.
至于可用性就这个硬件线程而言,你的操作系统不会撒谎,它正在忙着等待。然而,处理单元本身,忽略了正在向它爬行的缓存行,是事实上闲置.
在此期间未执行任何指令/操作/计算。
+----------+---------------+---------------------------------------------------------------------------------------------------+
| Type of | size of | Latency due to fetching a cache line |
| mem / op | cache +--------+--------+------------+--------------------------------------------------------------------+
| | (register) | clock | real | normalized | now I feel it |
| | | cycles | time | | |
+----------+---------------+--------+--------+------------+--------------------------------------------------------------------+
| tick | 16KB | 1 | 0.25ns | 1s | Dinner is already served. Sit down, enjoy. |
| | *the* 64 Bits | | | | |
+----------+---------------+--------+--------+------------+--------------------------------------------------------------------+
| L1 | 64KB | 4 | 1ns | 4s | Preparations are done, food's cooking. |
| | | | | | Want a cold one to bridge the gap? |
+----------+---------------+--------+--------+------------+--------------------------------------------------------------------+
| L2 | 2048KB | 11 | ~3ns | 12s | Would you be so kind as to help me dice the broccoli? |
| | | | | | If you want a beer, you will have to go to the corner store. |
+----------+---------------+--------+--------+------------+--------------------------------------------------------------------+
| L3 | 8192KB | 39 | ~10ns | 40s | The car is in the shop, you'll have to get groceries by bike. |
| | | | | | Also, food ain't gonna cook itself, buddy. |
+----------+---------------+--------+--------+------------+--------------------------------------------------------------------+
| DRAM | ~20GB | 107 | ~30ns | 2min | First year of college. First day of the holiday weekend. |
| | | | | | Snow storm. The roommate's are with their families. |
| | | | | | You have a piece of toast, two cigarettes and 3 days ahead of you. |
+----------+---------------+--------+--------+------------+--------------------------------------------------------------------+
该系列芯片的延迟数据
Core-i7-9XX
(来源:Scott Meyers,2010 年)
结论 如果无法进行适当的测量,那么与其争论核心与时钟速度,不如对于多余的硬件预算,最安全的投资是 CPU 缓存大小。
因此,如果内存定期保持各个硬件线程处于空闲状态,那么更多的~cow bell~核心肯定是解决方案?
理论上,如果软件准备就绪,多线程/超线程可以要快
假设您正在查看过去几年的纳税申报表(例如),总共 8 年的数据。您每年(行)保存 12 个月的值(列)。
现在,一个字节可以容纳 256 个单独的值(因为它的 8 个单独的二进制数字,每个数字可以假设 2 个状态,从而导致8^2 = 256
不同状态的排列。无论货币是什么,256 都感觉有点偏低,无法代表工资数字的上限。此外,为了论证的目的,我们假设最小面额(“美分”)无关紧要(每个人都赚取主要面额的整数值)。最后,假设雇主意识到高层管理人员和普通员工之间的工资差距,因此将那些少数人置于完全不同的会计系统中。
因此,在这个简化的场景中,我们假设两倍于上述内存空间量,即 2 个字节(或“半字”),在unsigned
形式上使用时,即表示从 的范围[0, 2^16 = 65536)
,足以表达所有员工的月薪值。
因此,在您选择的语言/RDBS/OS 中,您现在拥有一个矩阵(某种二维数据结构,即“列表列表”),其值具有统一的数据大小(2 字节/16 位)。
在 C++ 中,这将是一个std::vector<std::vector<uint16_t>>
。我猜您也会在 Java 中使用vector
of vector
of 。short
现在,这是有奖问答:
假设您想要调整这 8 年的值以应对通货膨胀(或其他任意原因写入地址空间)。我们正在研究 16 位值的均匀分布。您需要访问矩阵中的每个值一次,读取它,修改它,然后将其写入地址空间。
遍历数据的方式重要吗?
答案是:是的,非常如此。如果您首先迭代行(内部数据结构),您将在并发执行环境中获得近乎完美的可扩展性。在这里,一个额外的线程,因此一半的数据在一个线程中,另一半的数据在另一个线程中,将使您的作业运行速度提高一倍。4 个线程?4 倍的性能提升。
但是如果你选择先做列,两个线程将运行你的任务明显慢。您将需要大约 10 个并行执行线程才能减轻 (!) 主要遍历方向的选择所带来的负面影响。只要您的代码在单个执行线程中运行,您就无法测量出差异。
+------+------+------+------+------+------+------+
| Year | Jan | Feb | Mar | Apr | ... | Dec |
+------+------+------+------+------+------+------+
| 2019 | 8500 | 9000 | 9000 | 9000 | 9000 | 9000 | <--- contiguous in memory
+------+------+------+------+------+------+------+
| 2018 | 8500 | 8500 | 8500 | 8500 | 8500 | 8500 | <--- 12 * 16Bit (2Byte)
+------+------+------+------+------+------+------+
| 2017 | 8500 | 8500 | 8500 | 8500 | 8500 | 8500 | <--- 3 * (4 * 16Bit = 64Bit (8Byte)
+------+------+------+------+------+------+------+
| ... | 8500 | 7500 | 7500 | 7500 | 7500 | 7500 | <--- 3 cache lines
+------+------+------+------+------+------+------+
| 2011 | 7500 | 7200 | 7200 | 7200 | 7200 | 7200 | <--- 3 lines, likely from the same
+------+------+------+------+------+------+------+ virtual memory page, described by
the same page block.
OP写道: a) 具有 32 个内核且时钟速度为 2.5 Ghz 的 CPU
或
b) 具有 8 个内核但时钟速度为 3.8 Ghz 的 CPU
在其他条件相同的情况下:
-->考虑缓存大小、内存大小、硬件的推测预取功能以及能够真正利用并行化的运行软件,这些都比时钟速度更重要。
--> 即使不依赖第三方分布式系统,确保在生产条件下确实不受 I/O 限制。如果你必须拥有内部硬件,而不能让 AWS / GCloud / Azure / Heroku / Whatever-XaaS-IsHipNow 处理这种麻烦,那么就花钱购买你放置数据库的 SSD。虽然你不是希望数据库与应用程序位于同一台物理机器上,请确保网络距离(在这里也测量延迟)尽可能短。
--> 选择一个知名、经过审查、顶级的“企业级”HTTP 服务器库,并且毫无疑问是为并发而构建的,但这还不够。确保您在路由中运行的任何第三方库都是如此。确保您的内部代码也是如此。
在这种情况下,虚拟机或云解决方案不是一种选择
我明白了。
有各种正当理由。
它一定要是A物理机 [...]
[...] 具有 32 个内核且时钟速度为 2.5 Ghz 的 CPU
但事实并非如此。AWS
和 Azure 都没有发明分布式系统、微集群或负载平衡。在裸机硬件上进行设置更加痛苦,而且没有 MegaCorp 式的资源,但你能在您自己的客厅中运行分布式 K8 集群网格。此外,自托管项目还提供了用于定期进行健康检查和在峰值负载时自动配置的工具。
OP写道: RAM 并不重要
这是一个~假设~可重现的场景:启用 zram 作为交换空间,因为 RAM 很便宜而且不重要。现在运行一个稳定的、内存密集型的任务,不会导致频繁的分页。当你达到严重的 LRU 反转点时,你的风扇会变得很响,你的 CPU 核心会很热——因为它正忙于处理内存管理(将垃圾移入和移出交换区)。
OP写道: RAM 并不重要
如果我没有足够清楚地表达自己的话:我认为你应该重新考虑这个观点。
TL;DR?
32 核。
更多是更好的。