[Performance] 从“自旋锁”到“无锁原子化”:一次性能优化的解剖
发表于 2026-04-10
从“自旋锁”到“无锁原子化”:一次性能优化的解剖
1. QA上报开启虚拟域后性能巨幅下降
QA上报说,不开启虚拟域时性能正常,开启后,性能急剧下降,不到关闭时性能的20% 。 如此大的性能下降必然是有明显的瓶颈限制了性能。于是拿到环境,进行了性能分析。
1.1 top显示,性能都消耗在软中断处理中了。
Mem: 7946832K used, 123526396K free, 4303094K shrd, 184720K buff, 4492132K cach
CPU0: 0.0% usr 0.0% sys 0.0% nic 0.0% idle 0.0% io 0.0% irq 100% sirq
CPU1: 0.0% usr 0.0% sys 0.0% nic 0.0% idle 0.0% io 0.0% irq 100% sirq
CPU2: 0.0% usr 0.0% sys 0.0% nic 0.0% idle 0.0% io 0.0% irq 100% sirq
CPU3: 0.0% usr 0.0% sys 0.0% nic 0.0% idle 0.0% io 0.0% irq 100% sirq
CPU4: 0.0% usr 0.0% sys 0.0% nic 0.0% idle 0.0% io 0.0% irq 100% sirq
CPU5: 0.0% usr 0.0% sys 0.0% nic 0.0% idle 0.0% io 0.0% irq 100% sirq
CPU6: 0.0% usr 0.0% sys 0.0% nic 0.0% idle 0.0% io 0.0% irq 100% sirq
CPU7: 0.0% usr 0.0% sys 0.0% nic 0.0% idle 0.0% io 0.0% irq 100% sirq
CPU8: 0.0% usr 0.0% sys 0.0% nic 0.0% idle 0.0% io 0.0% irq 100% sirq
CPU9: 0.0% usr 0.0% sys 0.0% nic 0.0% idle 0.0% io 0.0% irq 100% sirq
CPU10: 0.0% usr 0.0% sys 0.0% nic 0.0% idle 0.0% io 0.0% irq 100% sirq
CPU11: 0.0% usr 0.0% sys 0.0% nic 0.0% idle 0.0% io 0.0% irq 100% sirq
CPU12: 0.0% usr 16.6% sys 0.0% nic 0.0% idle 0.0% io 0.0% irq 83.3% sirq
CPU13: 0.0% usr 0.0% sys 0.0% nic 0.0% idle 0.0% io 0.0% irq 100% sirq
CPU14: 0.0% usr 0.0% sys 0.0% nic 0.0% idle 0.0% io 0.0% irq 100% sirq
CPU15: 0.0% usr 0.0% sys 0.0% nic 0.0% idle 0.0% io 0.0% irq 100% sirq
CPU16: 0.0% usr 0.0% sys 0.0% nic 0.0% idle 0.0% io 0.0% irq 100% sirq
CPU17: 0.0% usr 0.0% sys 0.0% nic 0.0% idle 0.0% io 0.0% irq 100% sirq
CPU18: 0.0% usr 0.0% sys 0.0% nic 0.0% idle 0.0% io 0.0% irq 100% sirq
CPU19: 0.0% usr 0.0% sys 0.0% nic 0.0% idle 0.0% io 0.0% irq 100% sirq
CPU20: 0.0% usr 0.0% sys 0.0% nic 0.0% idle 0.0% io 0.0% irq 100% sirq
CPU21: 0.0% usr 0.0% sys 0.0% nic 0.0% idle 0.0% io 0.0% irq 100% sirq
CPU22: 0.0% usr 0.0% sys 0.0% nic 0.0% idle 0.0% io 0.0% irq 100% sirq
CPU23: 0.0% usr 0.0% sys 0.0% nic 0.0% idle 0.0% io 0.0% irq 100% sirq
CPU24: 0.0% usr 0.0% sys 0.0% nic 0.0% idle 0.0% io 0.0% irq 100% sirq
CPU25: 0.0% usr 0.0% sys 0.0% nic 0.0% idle 0.0% io 0.0% irq 100% sirq
CPU26: 0.0% usr 0.0% sys 0.0% nic 0.0% idle 0.0% io 0.0% irq 100% sirq
CPU27: 0.0% usr 0.0% sys 0.0% nic 0.0% idle 0.0% io 0.0% irq 100% sirq
CPU28: 0.0% usr 0.0% sys 0.0% nic 0.0% idle 0.0% io 0.0% irq 100% sirq
CPU29: 0.0% usr 0.0% sys 0.0% nic 0.0% idle 0.0% io 0.0% irq 100% sirq
CPU30: 0.0% usr 0.0% sys 0.0% nic 0.0% idle 0.0% io 0.0% irq 100% sirq
CPU31: 0.0% usr 0.0% sys 0.0% nic 0.0% idle 0.0% io 0.0% irq 100% sirq
1.2 perf top 显示性能主要消耗在queued_spin_lock_slowpath,说明有严重的锁竞争
PerfTop: 130619 irqs/sec kernel:99.6% exact: 100.0% [4000Hz cycles:ppp], (all, 32 CPUs)
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
77.48% [kernel] [k] queued_spin_lock_slowpath
2.35% [kernel] [k] do_raw_spin_lock
1.50% [kernel] [k] ipt_do_table
1.45% [kernel] [k] virtual_space_l4_traffic_rlimit
0.86% [kernel] [k] i40e_napi_poll
0.83% [kernel] [k] fib_table_lookup
0.61% [kernel] [k] ip_vs_conn_get
0.55% [kernel] [k] ip_rcv
0.47% [kernel] [k] __dev_queue_xmit
2 深入分析
2.1 perf record采样分析
为了获取更详细的信息,我使用 perf record 对系统运行时的 CPU 进行采样记录,并通过 perf report 对采样数据进行分析,查看热点函数和调用栈分布。
发现问题集中在 virtual_space_l4_traffic_rlimit 的虚拟域限速处理上。但是设备上并没有限速配置,也就是说 virtual_space_l4_traffic_rlimit 限速逻辑空跑就已经导致了性能下降。
perf report文件中 对 queued_spin_lock_slowpath 的调用主要由 virtual_space_l4_traffic_rlimit 发起,超多并发导致了锁竞争 。
部分report如下
--3.01%--__netif_receive_skb
|
--3.01%--__netif_receive_skb_core
|
--2.97%--ip_rcv
|
--2.80%--ip_rcv_finish
|
|--1.83%--ip_forward
| |
| --1.82%--nf_hook_slow
| |
| --1.82%--ip_vs_reply4
| |
| --1.82%--ip_vs_out.part.0
| |
| --1.79%--handle_response.constprop.0
| |
| --1.67%--virtual_space_l4_traffic_rlimit
| |
| --1.64%--__lock_text_start
| |
| --1.63%--do_raw_spin_lock
| |
| --1.61%--queued_spin_lock_slowpath
|
--0.86%--ip_local_deliver
|
--0.86%--nf_hook_slow
|
--0.83%--ip_vs_remote_request4
|
--0.83%--ip_vs_in.part.0
|
--0.82%--ip_vs_in_cont
|
--0.76%--virtual_space_l4_traffic_rlimit
|
--0.75%--__lock_text_start
|
--0.75%--do_raw_spin_lock
|
--0.73%--queued_spin_lock_slowpath
2.2 分析 virtual_space_l4_traffic_rlimit
2.2.1 第一个spin lock net->virtual_space_rlimit_hash_lock
查看 virtual_space_l4_traffic_rlimit 函数,我们发现,它上来就直接调用 spin_lock_irqsave(&net->virtual_space_rlimit_hash_lock, flags), 整个函数处理都在自旋锁的保护下。 而 virtual_space_l4_traffic_rlimit 又是在datapath,导致每个packet都要竞争spin lock。多核并发处理下,锁竞争严重影响到了包处理性能。
经分析这个lock 只是为了保证 discard计数的并发更新。实际上我们可以考虑对计数使用atomic原子操作,这样此处就不需要spin lock了。
2.2.2 第二个 spin lock rl->lock
在进一步处理中,函数尝试获取 raw_spin_trylock_irqsave(&rl->lock, flags) 来进行后续操作。同样也严重影响到性能。 经分析这个lock 只是为了保证 printed/missed 的更新。我们同样可以考虑对计数使用atomic原子操作,这样此处就不需要spin lock了。
3. 优化难点
虽然一些统计计数,我们可以用原子atomic操作替代,避免使用spin lock。 但是限速窗口刷新必须保证只能由一个CPU执行,这是一个难点。为了避免窗口刷新并发,我们添加了一个refreshing 标志。 通过原子 atomic_cmpxchg 保证只有一个CPU 进入到刷新处理。其他CPU会短暂循环等待,因为刷新默认是1秒,不像per packet处理那么频繁,并发概率较低,所以几乎可以忽略不记。
for (;;) {
/* 其他CPU在这里获取到新的begin */
start = READ_ONCE(rl->begin);
if (start && time_before(jiffies, start + interval))
return;
/* 第一个刷新的 set refreshing, 后面的等待刷新完成 */
if (atomic_cmpxchg(&rl->refreshing, 0, 1)) {
cpu_relax();
continue;
}
/* 只有一个CPU可以走到这一步 */
atomic64_set(&rl->printed, 0);
atomic_set(&rl->missed, 0);
/* 更新 begin */
WRITE_ONCE(rl->begin, jiffies);
/* 刷新完成 unset refressing. */
smp_store_release(&rl->refreshing.counter, 0);
}
有了这个处理就能保证 窗口刷新没有冲突。配合atomic 原子操作更新计数。我们就可以将原因spin lock删除,实现无锁处理。
4. 性能验证
debug image交由QA验证,性能大幅提升,接近 不开启 virtual space时的性能。 优化前 开启virtual space 是不开启 virtual space的 20% ,约为1/5 。 优化后 开启virtual space 是不开启 virtual space的 95% 。 优化后 开启virtual space的性能是 优化前 开启virtual space 性能的 5倍。
4.1 查看 perf top
再次对比 perf top,已经没有 queued_spin_lock_slowpath 了
PerfTop: 128592 irqs/sec kernel:99.0% exact: 100.0% lost: 0/0 drop: 0/0 [4000Hz cycles], (all, 32 CPUs)
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
9.13% [kernel] [k] ipt_do_table
7.00% [kernel] [k] __dev_queue_xmit
4.21% [kernel] [k] fib_table_lookup
3.20% [kernel] [k] i40e_napi_poll
2.39% [kernel] [k] i40e_lan_xmit_frame
2.11% [kernel] [k] handle_response
5. 后记
spin lock能够解决并发问题,但在某些场合因为并发冲突,导致性能下降。 原子操作、无锁处理才是更优的解决方案。
本文访问次数:... 次