0x00
最近将一个在CentOS/RHEL 7.x
下开发、运行的程序尝试移植到Ubuntu 16.04
下,发现性能骤降,用perf top
检查发现__vdso_clock_gettime
和read_hpet
的时间开销非常巨大。
0x01
Google搜了一圈:Two frequently used system calls are ~77% slower on AWS EC2
感觉是__vdso_clock_gettime
没有能按照预期工作,而是每次都进内核读取硬件时钟。
但是我们的环境都是baremetal,并不是VM,理论上不应该是上述文中提到的原因。
0x02
因为代码中不少地方使用了clock_gettime
和asio
的timer
,跟踪下去发现asio
的底层使用了HPET,但是每个io_service
仅使用了一个timerfd
,大量的read_hpet
应该是由clock_gettime
调用的,一般来说clock_gettime
不应该使用HPET这么高精度的时钟源。
顺着这个思路,发现两台机器的时钟源果然不同
$ cat /sys/devices/system/clocksource/clocksource0/current_clocksource
出问题的这台显示是hpet
,而正常应该为tsc
。
再查看当前可用时钟源,发现竟然没有tsc
!
$ cat /sys/devices/system/clocksource/clocksource0/available_clocksource
离真相已经不远了。
0x03
出问题的是Intel Core i9-7900x(代号Skylake-X),怀疑是内核对新CPU的支持还不完善。
在 Kernel.org 的 Bugzilla 和 git log 里面发现类似的反馈:
- Bug 197843 – native_calibrate_tsc(): possibly incorrect TSC frequency on newest Intel Skylake-X CPUs, i7-7820X in particular
- x86/tsc: Fix erroneous TSC rate on Skylake Xeon
Skylake-X的TSC晶振频率应该是25MHz,不是24MHz。内核在检测TSC时发现偏差,从而禁用了TSC,转用了HPET作为默认时钟源。
0x04
解决方案就是换最新的内核,我选用的是4.15.3
,再验证可用时钟源和当前时钟源均为tsc
无误,程序运行也恢复正常。
PS:至于开头__vdso_clock_gettime
为何没有按照预期运行,而是每次都执行read_hpet
,暂时未深入研究具体代码,以后有空再补……