brpc icon indicating copy to clipboard operation
brpc copied to clipboard

bthread signal & wait 问题咨询

Open MJY-HUST opened this issue 1 year ago • 24 comments

image

在使用过程中,观察server侧的火焰图,此时brpc_worker thread的cpu基本打满。发现futex_wake、futex_wait占用的时间占比比较高,竞争锁成为了瓶颈。怀疑可能是由于调度不均衡,某些worker中的task较多,而一些worker的task较少,当这些task消费完之后每次都会调用wait,然后又快速被signal唤醒。同时也存在parking_lot中没有wait的线程但仍然在signal的场景。

这方面是否存在一些优化点,比如:

  1. signal时判断是否存在wait状态的线程;
  2. wait之前先尝试steal_task n 次,如果都失败再wait挂起线程;或者wait时设置timeout时间而不是每次都是永久wait,只能被signal主动唤醒。

MJY-HUST avatar Dec 13 '24 10:12 MJY-HUST

Related issues: #2817 Related PR: #2819 #2398

chenBright avatar Dec 13 '24 11:12 chenBright

这个是一个普遍问题,我们线上也遇到了。有状态服务一般会独占机器,现在机器普遍核数都比较多,基本都100+核数了。目前看到brpc在bthread worker比较多的时候调度开销比较大,希望社区能尽快解决一下哈~ ❤️

icexin avatar Dec 17 '24 10:12 icexin

可以试试分组 https://github.com/apache/brpc/blob/master/docs/cn/bthread_tagged_task_group.md

yanglimingcn avatar Dec 17 '24 11:12 yanglimingcn

我看现在parking_lot的数量是写死的,一个tag内只有四个;一旦worker数量多了,futex中锁的竞争就上来了,想问一下这个值改成一个可配的参数或者根据tag所属的worker总数来动态调大会有什么负面影响吗?(比如parking_lot的数量 = bthread_concurrency_by_tag / 4 or 8)。 我自己做了一些测试,除了上述的做法之外,减少无效的signal和在wait前先steal一次也能解决一部分问题。 相同的负载: 启动四个rpc_press ./rpc_press -proto=echo_c++/echo.proto -method=example.EchoService.Echo -input='{"message":"hello"}' -qps=100000 -server=0.0.0.0:8000 -timeout_ms=-1 服务端使用example/echo_c++中的,server,worker线程数量为64个,但是在服务内增加了使用bthread_start_background创建两个bthread来模拟触发signal的行为,优化前后: 前: image image image

后:parking_lot改为16个 image image image

MJY-HUST avatar Dec 17 '24 11:12 MJY-HUST

可以试试分组 https://github.com/apache/brpc/blob/master/docs/cn/bthread_tagged_task_group.md

分tag得把N个tag(brpc::Server)绑到N个port吧,不太优雅

GreateCode avatar Dec 17 '24 11:12 GreateCode

可以试试分组 https://github.com/apache/brpc/blob/master/docs/cn/bthread_tagged_task_group.md

分tag得把N个tag(brpc::Server)绑到N个port吧,不太优雅

是的,需要这么做,如果各个Server之间没有要求很强的隔离性,那确实也不用这么做。本质上是得优化协程调度。

yanglimingcn avatar Dec 18 '24 08:12 yanglimingcn

据我观察,频繁的触发 futex_wake 和 futex_wait 的原因是因为整个系统在“不必要”的时机进入的休眠状态,这样很容易产生一个 wake 和 多个 wait,导致系统调用频繁,worker 越多,wait 和 wake 越频繁,所以减少如何减少这种“不必要性”才是最重要的。我这边经过大量的测试发现一个 case 就是整个系统在高压繁忙下也进入了 wait 状态,很明显这是“不合适的”,“不必要的”。这是由于当前的模型下,会导致 epoll 延后处理,形成了多个 worker 工作在一个 queue 上,queue 没有了,立刻都进入休眠,然后 epoll 被唤醒后,“worker 们”又相继被唤醒,造成了”不必要性“。

zhengJade avatar Jan 07 '25 03:01 zhengJade

据我观察,频繁的触发 futex_wake 和 futex_wait 的原因是因为整个系统在“不必要”的时机进入的休眠状态,这样很容易产生一个 wake 和 多个 wait,导致系统调用频繁,worker 越多,wait 和 wake 越频繁,所以减少如何减少这种“不必要性”才是最重要的。我这边经过大量的测试发现一个 case 就是整个系统在高压繁忙下也进入了 wait 状态,很明显这是“不合适的”,“不必要的”。这是由于当前的模型下,会导致 epoll 延后处理,形成了多个 worker 工作在一个 queue 上,queue 没有了,立刻都进入休眠,然后 epoll 被唤醒后,“worker 们”又相继被唤醒,造成了”不必要性“。

你的PR #2819 先合下?

GreateCode avatar Jan 07 '25 06:01 GreateCode

据我观察,频繁的触发 futex_wake 和 futex_wait 的原因是因为整个系统在“不必要”的时机进入的休眠状态,这样很容易产生一个 wake 和 多个 wait,导致系统调用频繁,worker 越多,wait 和 wake 越频繁,所以减少如何减少这种“不必要性”才是最重要的。我这边经过大量的测试发现一个 case 就是整个系统在高压繁忙下也进入了 wait 状态,很明显这是“不合适的”,“不必要的”。这是由于当前的模型下,会导致 epoll 延后处理,形成了多个 worker 工作在一个 queue 上,queue 没有了,立刻都进入休眠,然后 epoll 被唤醒后,“worker 们”又相继被唤醒,造成了”不必要性“。

你的PR #2819 先合下?

好的,已经在修改中了,刚空出时间来搞

zhengJade avatar Jan 07 '25 06:01 zhengJade

据我观察,频繁的触发 futex_wake 和 futex_wait 的原因是因为整个系统在“不必要”的时机进入的休眠状态,这样很容易产生一个 wake 和 多个 wait,导致系统调用频繁,worker 越多,wait 和 wake 越频繁,所以减少如何减少这种“不必要性”才是最重要的。我这边经过大量的测试发现一个 case 就是整个系统在高压繁忙下也进入了 wait 状态,很明显这是“不合适的”,“不必要的”。这是由于当前的模型下,会导致 epoll 延后处理,形成了多个 worker 工作在一个 queue 上,queue 没有了,立刻都进入休眠,然后 epoll 被唤醒后,“worker 们”又相继被唤醒,造成了”不必要性“。

你的PR #2819 先合下?

好的,已经在修改中了,刚空出时间来搞

啥时候合,再催一下哈哈哈

zhoukangsheng avatar Mar 04 '25 02:03 zhoukangsheng

@zhengJade

GreateCode avatar Mar 04 '25 07:03 GreateCode

@MJY-HUST 可以试试#2907 看能不能解决问题?

chenBright avatar Mar 14 '25 02:03 chenBright

@MJY-HUST 可以试试#2907 看能不能解决问题?

没效果

GreateCode avatar Mar 17 '25 09:03 GreateCode

@MJY-HUST 可以试试#2907 看能不能解决问题?

没效果

@GreateCode #2819 呢?

chenBright avatar Mar 17 '25 09:03 chenBright

@MJY-HUST 可以试试#2907 看能不能解决问题?

没效果

@GreateCode #2819 呢?

这个改动较大,我摘出来测试下

GreateCode avatar Mar 17 '25 09:03 GreateCode

@MJY-HUST 可以试试#2907 看能不能解决问题?

没效果

@GreateCode #2819 呢?

这个改动较大,我摘出来测试下

#2819 确实优化了性能,但从测试结果看,也存在调大pthread,同qps消耗cpu变大的情况。 我感觉可能是bthread worker的wait/wake成本过大导致的,比如前者成本是5,bthread处理成本是10,有2个bthread在一个bthread worker处理消耗cpu 是20,但若被其他bthread worker steal走,则成本是10 + (10 + 5) = 25。 所以最好是若bthread worker的bthread queue不长,就别让其他bthread worker steal了,或者queue很长,一次性steal多个,可能bthread latency可能增大,但节省的cpu消耗是更期望。

这是epoll优先处理的优化效果 Image

这是在epoll优化基础上调整pthread的效果

Image

Image

GreateCode avatar Apr 01 '25 09:04 GreateCode

@chenBright 帮忙看看?或者继续优化下?

GreateCode avatar Apr 02 '25 08:04 GreateCode

类似go的 steal half是不是更好一些

GreateCode avatar Apr 02 '25 08:04 GreateCode

类似go的 steal half是不是更好一些

steal 多个的时候,在流量不高的情况下,会造成同一个任务被两个 pthread 反复偷的情况,A 偷了 10 个,做到 一半,可能又被 B 偷回去了。而且由于任务的执行时间长短不同,一刀切的偷多个,可能导致 steal成本增加。

zhengJade avatar Apr 02 '25 08:04 zhengJade

若queue不长(可配),就别唤醒其他bthread_worker了,可以减少上下文切换。 epoll opt优化了很多上下文切换,应该还有优化空间。

Image

flamegraph_process_context-switches_130pthread: Image

GreateCode avatar Apr 02 '25 09:04 GreateCode

类似go的 steal half是不是更好一些

steal 多个的时候,在流量不高的情况下,会造成同一个任务被两个 pthread 反复偷的情况,A 偷了 10 个,做到 一半,可能又被 B 偷回去了。而且由于任务的执行时间长短不同,一刀切的偷多个,可能导致 steal成本增加。

参考下这里https://github.com/golang/go/blob/go1.24.1/src/runtime/proc.go#L6983

GreateCode avatar Apr 02 '25 09:04 GreateCode

#2819 #2916 #2907 这几个PR能改善这个问题了吗?

yanglimingcn avatar May 22 '25 11:05 yanglimingcn

#2819 #2916 #2907 这几个PR能改善这个问题了吗?

4.2号测了一次,也是这几个PR,效果没彻底解决;这几个PR可能有改动,我抽空再跑一次

GreateCode avatar Jun 06 '25 02:06 GreateCode

我看现在parking_lot的数量是写死的,一个tag内只有四个;一旦worker数量多了,futex中锁的竞争就上来了,想问一下这个值改成一个可配的参数或者根据tag所属的worker总数来动态调大会有什么负面影响吗?(比如parking_lot的数量 = bthread_concurrency_by_tag / 4 or 8)。 我自己做了一些测试,除了上述的做法之外,减少无效的signal和在wait前先steal一次也能解决一部分问题。 相同的负载: 启动四个rpc_press ./rpc_press -proto=echo_c++/echo.proto -method=example.EchoService.Echo -input='{"message":"hello"}' -qps=100000 -server=0.0.0.0:8000 -timeout_ms=-1 服务端使用example/echo_c++中的,server,worker线程数量为64个,但是在服务内增加了使用bthread_start_background创建两个bthread来模拟触发signal的行为,优化前后: 前: image image image

后:parking_lot改为16个 image image image

@MJY-HUST @GreateCode 试试#3033

chenBright avatar Jul 20 '25 02:07 chenBright