我正在我们的 Storm Supervisor(Wheezy 机器)上调试高系统 CPU 使用率(不是用户 CPU 使用率)。以下是观察结果
相关进程的 perf 输出:
Events: 10K cpu-clock
16.40% java [kernel.kallsyms] [k] system_call_after_swapgs
13.95% java [kernel.kallsyms] [k] pvclock_clocksource_read
12.76% java [kernel.kallsyms] [k] do_gettimeofday
12.61% java [vdso] [.] 0x7ffe0fea898f
9.02% java perf-17609.map [.] 0x7fcabb8b85dc
7.16% java [kernel.kallsyms] [k] copy_user_enhanced_fast_string
4.97% java [kernel.kallsyms] [k] native_read_tsc
2.88% java [kernel.kallsyms] [k] sys_gettimeofday
2.82% java libjvm.so [.] os::javaTimeMillis()
2.39% java [kernel.kallsyms] [k] arch_local_irq_restore
在相关进程的线程中捕获了这个
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
100.00 0.000247 0 64038 gettimeofday
0.00 0.000000 0 1 rt_sigreturn
0.00 0.000000 0 1 futex
------ ----------- ----------- --------- --------- ----------------
100.00 0.000247 64040 total
最后发现线程运行在while(true)
其中一个电话是System.currentTimeMillis()
。我禁用了相同的功能,系统 CPU % 从 50% 下降到 3%。很明显这就是问题所在。我不明白的是,在存在 vDSO 的情况下,这些内核调用应该只发生在用户的地址空间中。但从性能报告中可以清楚地看出,内核调用确实是在内核空间中发生的。有这方面的指点吗?
内核版本:3.2.0-4-amd64 Debian 3.2.86-1 x86_64 GNU/Linux
时钟类型:kvm
添加有问题的线程的代码。
@RequiredArgsConstructor
public class TestThread implements Runnable {
private final Queue<String> queue;
private final Publisher publisher;
private final int maxBatchSize;
private long lastPushTime;
@Override
public void run() {
lastPushTime = System.currentTimeMillis();
List<String> events = new ArrayList<>();
while (true) {
try {
String message = queue.poll();
long lastPollTime = System.currentTimeMillis();
if (message != null) {
events.add(message);
pushEvents(events, false);
}
// if event threshold hasn't reached the size, but it's been there for over 10seconds, push it.
if ((lastPollTime - lastPushTime > 10000) && (events.size() > 0)) {
pushEvents(events, true);
}
} catch (Exception e) {
// Log and do something
}
}
}
private void pushEvents(List<String> events, boolean forcePush) {
if (events.size() >= maxBatchSize || forcePush) {
pushToHTTPEndPoint(events);
events.clear();
lastPushTime = System.currentTimeMillis();
}
}
private void pushToHTTPEndPoint(List<String> events) {
publisher.publish(events);
}
}
我不明白的是,在存在 vDSO 的情况下,这些内核调用应该只发生在用户的地址空间中。但从性能报告中可以清楚地看出,内核调用确实是在内核空间中发生的。有这方面的指点吗?
可以在虚拟系统上禁用 vDSO。 KVM 使用 PVClock(您可以在这篇文章中阅读更多相关内容)article https://blog.packagecloud.io/eng/2017/03/08/system-calls-are-much-slower-on-ec2/),这取决于内核版本。
例如,我们可以看到here https://github.com/torvalds/linux/blob/v3.2/arch/x86/vdso/vclock_gettime.cVCLOCK_MODE 永远不会被覆盖。
另一方面,here https://github.com/torvalds/linux/blob/v3.8/arch/x86/kernel/kvmclock.c#L298它被更改为 vclock_mode - 和 vclock_mode指标 https://github.com/torvalds/linux/blob/v3.2/arch/x86/vdso/vclock_gettime.c#L161对于 vDSO 也是如此。
此支持是在此引入的commit https://github.com/torvalds/linux/commit/3dc4f7cfb7441e5e0fed3a02fc81cdaabd28300a并在Linux内核3.8版本中发布。
一般来说,在我的实践中,如果你长时间调用“while(true)”内部的某些东西,你总是会看到很大的CPU消耗。
当然,在大多数情况下,阻塞队列就足够了,但是如果您需要良好的延迟和性能,您也可以使用旋转,而不需要线程阻塞,但您应该限制旋转周期并制定基准来衡量此优化的影响。元代码可能类似于:
int spin = 100;
while(spin-- > 0) {
// try to get result
}
// still no result -> execute blocking code
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)