我对调整linux的java性能有一个很大的问题,所以我从我的案例开始。
我有一个应用程序运行多个相互通信的线程。我的典型工作流程是:
1)一些消费者线程在公共对象锁上同步并在其上调用wait()
2)一些生产者线程通过Selector等待来自网络的数据
2.1)生产者接收数据并形成一个具有接收时间戳(微秒精度)的对象
2.2)生产者将这个数据包放在一些交换地图中,并在普通锁上调用notifyAll
3)消费者线程唤醒并读取生成的对象
3.1)consumer创建新对象并在其中写入接收时间戳和当前时间戳之间的微秒时间差。这样我就可以监控反应时间。
这个反应时间是整个问题。
当我在自己的机器上测试我的应用程序时,我通常会得到大约200-400微秒的反应时间,但是当我在我的生产linux机器上监视它时,我会得到2000到4000微秒的数字!
现在我正在运行ubuntu 16.04作为我的生产操作系统和Oracle jdk 8-111。我有一个带有2个Xeon处理器的物理服务器。我在这台服务器上只运行常用的OS守护进程和我的应用程序,因此与我的开发笔记本相比,有足够的资源。
我将java应用程序作为带有标志的jar文件运行: sudo chrt -r 77 java -server -XX:+ UseNUMA -d64 -Xmx1500m -XX:NewSize = 1000m -XX:+ UseG1GC -jar ...
我使用sudo chrt来改变优先级,因为我认为是这种情况,但它没有帮助。
我调整了bios以获得最佳性能并关闭了C状态。
我可以调整哪些更快的反应时间和低上下文切换?
答案 0 :(得分:0)
不,Linux上没有单一的echo 1 > /proc/sys/unlock_concurrent_magic
选项来全局提高并发性能。如果存在这样的选项,则默认情况下会启用它。
事实上,尽管一般来说是可调的,但是你不会发现许多可调参数对Linux上的原始并发产生特别大的影响。可能有助于 incidentially 与并发相关的那些 - 即启用某些内容(让我们说THP)会减慢您的特定负载,并且因为至少部分缓慢发生在锁定之下,整个并发吞吐量都会受到影响。
然而,我的经验是,Java应用程序很少直接受操作系统级并发行为的影响。实际上,大多数Java并发都是在不使用OS功能的情况下高效实现的,并且在各个操作系统中的行为相同。 JVM与并发性相关的主要操作点是线程创建,销毁和等待/阻塞。最近的Linux内核在这三个方面都有非常好的实现,所以你遇到一个瓶颈是不寻常的(实际上,一个经过良好调整的应用程序不应该进行大量的线程创建,并且还应该寻求最小化阻塞)。
因此,我发现性能差异很可能是由于硬件,应用程序配置,应用负载或其他方面的其他差异造成的。
以下是我首先尝试描述您的开发主机与生产系统之间的性能差异。
在顶层,差异可能是因为生产堆栈实际上更慢对于有问题的负载,或者因为本地测试没有准确反映生产负荷。
您可以通过一个快速测试来区分这些情况,即运行正在运行的任何本地测试,以便在卸载的生产服务器上获得200-400us的响应时间。如果服务器仍的响应时间要差10倍,那么您就知道您的测试可能是合理的,而且差异实际上在生产堆栈中。
此时,问题可能仍然存在于操作系统,软件配置,硬件等中。因此,您应该尝试将生产堆栈与本地主机之间的差异一分为二 - 将任何可调参数设置为相同值,调查任何特定于应用程序的配置差异,尝试表征任何硬件差异。
一个重要的问题是,生产服务器通常采用多插槽配置,这可能会使争用成本增加一个数量级,因为需要跨插槽通信(通常为100多个周期) - 而开发盒通常是多核但是单插槽,因此共享L3包含争用开销(通常约为30个周期)。
另一方面,您可能会发现本地测试也在生产服务器上执行就好,因此真正的问题是您的测试并不能代表真正的生产负载。然后,您应该努力表征真正的生产负载,以便您可以复制它,然后在本地调整它。如何在本地调整它当然可以填满一两本书(或者需要一个或两个非常高薪的承包商),所以你必须回到一个较窄的问题来获得有用的帮助。
"大铁"这是一个常见的谬论。比你小巧的笔记本电脑更快。实际上,对于许多操作来说恰恰相反,尤其是在测量操作延迟时(而不是总吞吐量)。
例如,即使比较单插槽系统,服务器部件的内存延迟通常比客户端部件慢50%。 John McCalpin reports客户端Sandy Bridge部件的主内存延迟为54.6 ns,相应服务器部件的主内存延迟为79 ns。众所周知,服务器的内存和内存控制器设计路径可以消除吞吐量,可靠性以及支持更多内核和总DRAM 1 的延迟。
特别提到您的生产者服务器是" 2 Xeon处理器",我将其称为双插槽系统。一旦引入第二个套接字,就完全改变了同步的机制。在单核系统上,当争用单独的线程时,最坏的情况是您通过共享L3发送缓存行和一致性流量,共享L3的延迟为30-40个周期。
在具有多个套接字的系统上,并发流量通常必须流过套接字之间的QPI链路,其具有DRAM访问顺序的延迟,可能是80 ns(即3GHz盒上的240个周期) 。因此,仅从硬件架构上就可以减慢近一个数量级的速度。
此外,当您描述工作流时,notifyAll
类型的方案通常会因更多内核和更多线程而变得更糟。例如,使用更多内核,您不太可能在同一个超线程上运行两个通信进程(这极大地加速了线程间协调,但在其他方面是不可取的)并且总争用和一致性流量可能与数量成比例地扩大核心(例如,因为当你唤醒线程时,缓存行必须在每个核心上打乒乓)。
因此,通常情况下,严重争用(通常设计得很糟糕)的算法在“大铁”上的表现要差得多。而不是单插槽消费者系统。
1 例如,通过缓冲,增加了延迟,但增加了主机的最大RAM容量。