Hello Lock Contention

在并发编程里面,锁是一个逃不开的东西,对于很多多线程共享的资源,使用锁是一个非常的好的办法,来支持对这些资源安全的访问。但锁虽然方便,用不好就容易出现问题。

首先大家需要知道,对锁单纯的进行 Lock 和 Unlock 操作其实是很快的,但如果涉及到了 Lock Contention,那么性能就会有较大的影响了。

Rust Prometheus Metric Vector

最开始注意到 Lock Contention 问题应该是在我们使用 Rust Prometheus 库的时候,因为当时是按照 Go 的 official library 来设计的,所以对 Metric Vector 类型的我们也是直接一个读写锁来保证里面共享的 HashMap 的安全性。

因为库的细节很多同学不会关心,他们会自然的认为 Prometheus 的 API 设计的比较高效,所以在一些频繁需要 metrics 的地方,他们直接调用了 Prometheus 的 API,如果只是单线程还好,但悲催的是,很多逻辑是会涉及到多线程操作的,然后我们就发现性能下降非常的严重。譬如这个 PR 就是发现了这样的问题才临时修改的。

为了解决 Prometheus Metric Vector 的 Lock Contention 问题,我们现在临时的解决办法就是使用 Local Metrics,也就是先线程自己去记录 metrics,然后定期的 flush 到全局的 Prometheus metrics 里面,这样整体性能就好了很多,但其实这种做法很丑陋。后面我们准备参考 RocksDB 的做法,使用 Per CPU 的 metrics 方案,也就是每个 CPU 自己去统计 metrics。无论是不是多线程,同时一个线程的时间片就只可能在一个 CPU 上面,所以相关的 metrics 自然可以跟这个 CPU 关联,详细可以参考 Core-local Statistics 这篇文章。

虽然我之前说单纯的进行 Lock 和 Unlock 很快,但在一些特别高频的地方,还是会有影响,所以对于一些非常快速的操作,我们后面开始使用 SpinLock

Worker Thread Pool

上面说了 Prometheus Metrics 遇到的坑,再来说说最近遇到的第二个问题。大家知道,我们通常会写一个 worker thread pool 来并发的处理我们的任务,而通常的一个 thread pool 的实现方式,就是使用 mutex + condition var 了,这个实现的代码实在的太多了,大家直接 Google 吧,这里就不详细说明了。

在 TiKV 里面,我们也使用 mutex + condition var 来做了一个 thread pool,然后这周我在跟另一个线程调度问题,跟葱头讨论的时候,突然想 benchmark 下我们的 thread pool 的极限性能,先写了一个简单的测试,测试很简单,就是往一个 channel 里面发数据,然后还有一个独立的线程不停的收,每一秒统计收到的数据个数。

然后我开始 benchmark,悲催的发现,QPS 不到 200 K,也就是 TiKV 现在极限情况下面单个节点最多能处理 200 K 的请求,虽然还不错,但我总觉得可以在高一点。因为我们是 8 个 worker thread。如果调整成 4 个,性能会更好,这个是正常的,因为对于这种几乎没啥消耗的 task,CPU 执行起来非常的快,这时候反倒是多个线程的 context switch 会影响到整体性能,关于线程调度这块,以后我详细研究了在整理出来,这里先不讨论了。

然后我就想,仍然是 8 个 worker thread,如何还能性能提升?而且在测试的使用,使用 top -H 观察,会明显发现 CPU 打不满,而我的测试机器是 40 Core,不存在性能问题。我只能将我的目光锁定到了 mutex 的 Lock Contention 上面,因为在我们的实现中,给 task queue 添加任务以及从 worker thread 获取任务,都是在 mutex 里面进行的,也就是每次操作都会极大的概率遇到 Lock Contention。

于是我先做了第一个优化,将 task queue 从 mutex 里面移出来,使用 SpinLock,测试发现 QPS 上升到 230 K,然后用 perf record 两次测试,diff 对比:

# Event 'cycles:ppp'
#
# Baseline    Delta  Shared Object        Symbol
# ........  .......  ...................  ...................................................................................
#
     8.40%   +7.58%  [kernel.vmlinux]     [k] _raw_spin_lock
     3.29%   -0.17%  [kernel.vmlinux]     [k] __schedule
     1.02%   +0.09%  [kernel.vmlinux]     [k] futex_wait_setup
     0.96%   -0.06%  [kernel.vmlinux]     [k] finish_task_switch

发现之前的测试比 SpinLock 的多了 7.5% 的 _raw_spin_lock 开销,这个当然不是我们自己实现的 SpinLock,而是内核 Mutex 里面的。

但即使这样,我仍然发现,worker thread 的 CPU 跑不满,怀疑是 worker 线程取任务太快,而 Producer 线程没法那么快的把 task 加到队列里面,于是 worker 线程因为没有获取到任务,只能重新进入 condition wait 状态。于是我暴力的改了一下获取 task 的代码,循环 10 次,如果没取到任务,就调用 thread 的 yield 函数让出时间片,下一轮继续尝试。然后这次 QPS 涨了不少,直接到了 300 K+ 了,当然 CPU 也高了不少了。

但这个测试只是能评估我们极限性能,好指导我们后续的优化。譬如现在我就知道我们最多不到 200 K 的 QPS,如果还是这个模型,其它地方再怎么优化,也还是这么多。

说到这里,在聊一件性能测试的时候遇到的奇怪的事情,我在使用 perf stat 分析性能的时候,发现使用 SpinLock 的版本 QPS 能到 400 K,但之前的版本仍然不到 200 K。这个结果实话当时第一眼看到,是非常的奇怪的,后来思考了一下,因为 perf stat 其实是会在一些内核的地方进行 probe 的,这个会影响性能,可能因为这样,导致 worker thread 那边的 condition wait 以及调度变慢了,结果 Producer 那边扔进去更多的任务,使得 worker 后面一直能取到任务,不会进入 mutex 和 condition wait 了。而对于我们之前的版本来说,无论怎样,都要通过 mutex 来进行 task 操作,自然就不会出现 SpinLock 这样的情况。对于这个问题,这只是我的猜测,我也跟其他对 Linux 系统很精通的同学讨论过这个问题,大家都觉得这个解释比较合理,只是后面我也懒得去追下去了,等后面有空了,重新折腾调度的时候再看看吧。

Perf script

在测试的时候,我使用 perf trace 或者 perf record 大概知道了不同方案 mutex 的调用情况,但其实我最想知道的是到底出现了多少次 Lock Contention,因为我怀疑就是因为这个导致的性能差异。

虽然能通过 systemtap 来搞定这个问题,但我不知道为啥头脑抽风了,想用 perf 来搞定。开始想的是直接 perf stat 的时候传入相应的 event 来做,但我在 perf list 里面没找到对应的 event,Google 了下也没啥好的结果,这个其实还是我对底层了解的不够,真熟练了直接源码一翻就知道了。但幸运的是,我突然发现了 perf script,之前这个玩意我只是在生成火焰图的时候见过,但自己并没有用过。昨天才知道,它其实是一个非常强大的东西,也就是说,我们可以写自己的 script 来分析 perf 采样的数据,而使用的 script language 并不需要像 systemtap 那样重新学一门新的,就是 perl 或者 python,当然 perl 就算了,python 虽然我不喜欢,但至少之前还写过 2 年,完全够用了。具体的 perf script 的东西,后面有时间可以在详细的说明。

我使用 perf script -l 看现在有啥可用的脚本,幸运的发现了一个 futex-contention,直接使用 perf script report futex-contention 得到了如下的输出:

test[132777] lock 7faaf8a220c0 contended 199 times, 132761 avg ns
test[132782] lock 7faaf8a22090 contended 1171 times, 519948 avg ns
test[132778] lock 7faaf8a22090 contended 1145 times, 2004888 avg ns
test[132778] lock 7faaf8a220c0 contended 226 times, 1694905 avg ns
test[132779] lock 7faaf8a22090 contended 1199 times, 2745749 avg ns

然后再把这个结果贴到 Excel 里面,对 times 汇总,确定了 SpinLock 的版本比原来的版本少了将近 15% 的 Lock Contention。

总结

其实上面折腾了这么多,看起来没折腾出啥东西,无非就是知道了 Lock Contention 对性能会有影响,但对我来说,更大的收获在于在排查问题的时候,对系统理解更加深入,对 perf 这些工具用起来更加熟练了。

TiKV 开发到现在,很多调优工作其实已经涉及到了跟系统和硬件的结合上面,之前我们还能假设更底层对我们是一个黑盒,但现在为了更好的性能,需要我们对更下面抽丝剥茧,开始深入理解和掌握了。如果你对 Linux 非常感兴趣,想折腾下 DPDK 等跟硬件更紧密的优化开发,欢迎给我发邮件,我的邮箱 tl@pingcap.com

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,525评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,203评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,862评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,728评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,743评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,590评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,330评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,244评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,693评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,885评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,001评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,723评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,343评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,919评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,042评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,191评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,955评论 2 355

推荐阅读更多精彩内容