TiDB 多Socket 服务器性能扩展问题分析-续

【是否原创】是
【首发渠道】TiDB 社区
【首发渠道链接】
【正文】

在上一篇TiDB 多Socket 服务器性能扩展问题分析中提到,我们通过Perf C2C工具分析,认为这个问题很可能是由于CPU Cache line的false share造成的。 并定位到对同一cache line上不同数据的读写冲突的函数分别是:
runtime.heapBitsSetType
runtime.(*mspan).sweep
runtime.sweepone
runtime.(*mheap).reclaim
但由于Perf C2C工具并不支持关联到Go代码的位置,因此我们还无法定位到发生冲突的对应的结构、变量定义。

最近,在工作中我们发现英特尔的性能分析神器vtune是可以支持go应用的。我们就使用vtune工具来进一步分析这个问题。我们首先使用vtune的hotspot分析功能来对TiDB进行的profiling。和之前Perf的结果略有区别,vTune给出最热点的函数是runtime.heapBitsForAddr,占比23.4%。不过由于这个函数是被runtime.heapBitsSetType调用的,所以算在runtime.heapBitsSetType中也没问题。热点函数如下面这张图所示:

点击runtime.heapBitsForAddr,就可以关联到该函数对应的go源代码。特别强大的是,我们还能关联到这个函数对应的汇编代码。就像这张图:

由于在上一篇的分析中,我们借助perf c2c工具已经可以找到发生HITM时的代码地址。比如对runtime.heapBitsSetType函数来说,代码地址是0x10d786a。利用vtune的汇编代码视图,我们根据这个代码地址,就可以找到对应的汇编代码位置。比如,对于0x10d786a地址来说,就对应这样一条汇编指令。

在我们找到这条汇编指令后,vtune能够帮助我们找到这条汇编指令对应的go源代码,就是下面这行代码。

通过这个方法,我们可以分别找到:
runtime.(*mspan).sweep函数中的代码是:
“atomic.Xadd64(&mheap_.pagesSwept, int64(s.npages))”,

runtime.sweepone函数对应的代码是
“atomic.Xadduintptr(&mheap_.reclaimCredit, npages)”,runtime.

(*mheap).relcaim函数对应的代码是
“if atomic.Casuintptr(&h.reclaimCredit, credit, credit-take) {”。

用这个办法,我们可以很容易地确定cache line假共享和mheap结构中的pagesSwept、reclaimCredit以及arenas变量有关。如果,假设pagesSwept在cache line中的offset是0x00的话,则reclaimCredit位于0x30,arenas位于0x38。这刚好和上一篇中,Perf c2c工具输出发生HITM的cache line上的偏移对应。

到这里,我们比较确定地找到了TiDB在2路服务器上性能下降的根源在于Go中mheap结构的定义方式。一般解决cache line假共享问题的方法是在出现假共享的变量间增加一些padding,或改变变量的定义顺序,使发生冲突的变量分配到不同的Cache line上。

由于这个问题的根源是出现在go runtime中,我们向go社区提交了一个issuehttps://github.com/golang/go/issues/47831。让我们拭目以待这个问题的最终解决,以及TiDB最终能在2路的环境上获得多少的性能提升。

4赞

你是真的牛逼啊!

围观一下
不知bios层关闭掉numa,性能损失会不会小一些。

如果是伪共享导致的性能问题的话,是不是非numa,多核场景下也会有影响(cache miss)?只是没有numa下影响着么大吧。