apache / brpc

brpc is an Industrial-grade RPC framework using C++ Language, which is often used in high performance system such as Search, Storage, Machine learning, Advertisement, Recommendation etc. "brpc" means "better RPC".
https://brpc.apache.org
Apache License 2.0
16.56k stars 3.98k forks source link

epoll bthread deal first #2819

Open zhengJade opened 1 week ago

zhengJade commented 1 week ago

What problem does this PR solve?

对 brpc 性能做了提升

Problem Summary: 目前的 brpc 网络事件存在两个问题

  1. epoll 线程和其他 bthread 具有相同优先级,导致 epoll 线程可能被一小段时间在队列中无响应,引起 worker queue 任务数量分布不均匀,多对一的 steal。
  2. 网络事件无差别 signal,增加了系统调用。

What is changed and the side effects?

epoll 线程不在 queue 中,独立出来

Side effects:

压测结果

优化后

1w image


Check List:

yanglimingcn commented 1 day ago

能否在bthread这个层面上,创建协程的时候设置一个属性,使它具有更高的优先级呢?现在这里专门设置了epoll_XXX,感觉有点特化。 另外,总是让epoll的bthread优先调度的话,会不会造成堆积很多任务的情况呢?

chenBright commented 1 day ago

能否在bthread这个层面上,创建协程的时候设置一个属性,使它具有更高的优先级呢?现在这里专门设置了epoll_XXX,感觉有点特化。

+1。 进一步抽象出一个高优先级队列,使用WorkStealingQueue来实现:

  1. 简化实现逻辑。
  2. 后续还有高优先级的bthread,只需要设置对应的bthread_attrflags_t即可。

这样是不是更好呢?

chenBright commented 1 day ago

总是让epoll的bthread优先调度的话,会不会造成堆积很多任务的情况呢?

现在只有一个连接,epoll bthread调度不是很频繁。是不是可以起多一些client和epoll bthread再测一下呢?

wanghenshui commented 1 day ago

能否在bthread这个层面上,创建协程的时候设置一个属性,使它具有更高的优先级呢?现在这里专门设置了epoll_XXX,感觉有点特化。 另外,总是让epoll的bthread优先调度的话,会不会造成堆积很多任务的情况呢?

1 你说得对,是有点特化,这么改简单点

如果是带优先级的bthread 可能就得改调度了,涉及到任务抢占/饥饿问题,可能麻烦一点

2 你说的也对可能会堆积,但是epoll任务是比较少的

如果改了优先级,会存在你说的优先级高的任务太多导致其他任务饿死

chenBright commented 1 day ago

@zhengJade 延时性能的提升主要是因为epoll bthread优先调度了吗?

yanglimingcn commented 1 day ago

能否在bthread这个层面上,创建协程的时候设置一个属性,使它具有更高的优先级呢?现在这里专门设置了epoll_XXX,感觉有点特化。 另外,总是让epoll的bthread优先调度的话,会不会造成堆积很多任务的情况呢?

1 你说得对,是有点特化,这么改简单点

如果是带优先级的bthread 可能就得改调度了,涉及到任务抢占/饥饿问题,可能麻烦一点

这样的特化,可能在一些其它用户哪里有些不适用,要尽量保持和原来的兼容性。

2 你说的也对可能会堆积,但是epoll任务是比较少的

如果改了优先级,会存在你说的优先级高的任务太多导致其他任务饿死

epoll任务的多少,是由客户端那边决定的,这个不太好预期。

zhengJade commented 1 day ago

总是让epoll的bthread优先调度的话,会不会造成堆积很多任务的情况呢?

现在只有一个连接,epoll bthread调度不是很频繁。是不是可以起多一些client和epoll bthread再测一下呢?

zhengJade commented 1 day ago

能否在bthread这个层面上,创建协程的时候设置一个属性,使它具有更高的优先级呢?现在这里专门设置了epoll_XXX,感觉有点特化。 另外,总是让epoll的bthread优先调度的话,会不会造成堆积很多任务的情况呢?

@yanglimingcn 我觉得可以增加一个属性来表示高优先级,但是要说明一点,就是 epoll 的 任务被 steal 的前提肯定是有 worker 需要 steal from task_control,所以可证明 worker 自己的 queue 应该没有任务,证明他是空闲的,这个时候应该优先让其响应网络事件,这有两个好处

  1. 保证网络任务能够及时响应。
  2. 可以让所有的 worker 的 run_q 相对均匀,因为 q 中大量任务来源不是 steal,而是 deal event。 另一个问题,就是是否会造成堆积: 如果我们不响应 epoll 就会造成堆积,证明此刻的网络请求数量是大于 brpc 的处理能力的,否则我们不需要通过增加 steal 的次数这种比起 pop queue 更费时间的操作来加快某时刻处理存量 bthread 的能力。这是牺牲了 RT 带来的,感觉不合理。而且 QPS 高时候更容易造成 RT 抖动。
zhengJade commented 1 day ago

总是让epoll的bthread优先调度的话,会不会造成堆积很多任务的情况呢?

现在只有一个连接,epoll bthread调度不是很频繁。是不是可以起多一些client和epoll bthread再测一下呢?

好的,我再测试一下多 client 的效果

zhengJade commented 1 day ago

@zhengJade 延时性能的提升主要是因为epoll bthread优先调度了吗?

@chenBright 从理论上看应该有两方面原因

  1. epoll 响应更及时,导致整个 brpc 的处理优先级逻辑发生了变化,以前是所有的 worker 尽量先处理完所有的任务,才能保证有新的 event 进来,当然,没处理完也有会,这取决于是否 steal 到了 epoll 任务,这带来的变化就是,所有空闲的 worker 会立刻拿到 epoll,然后填充自己的 queue,这样可以保证,每个 worker 的 queue 都有一定量的任务,减少 steal 的频率。
  2. 任务分布均匀后,wait 的频率降低,可以降低 signal 的频率,只有有任务等待在通知。减少系统调用。
zhengJade commented 1 day ago

能否在bthread这个层面上,创建协程的时候设置一个属性,使它具有更高的优先级呢?现在这里专门设置了epoll_XXX,感觉有点特化。 另外,总是让epoll的bthread优先调度的话,会不会造成堆积很多任务的情况呢?

1 你说得对,是有点特化,这么改简单点

如果是带优先级的bthread 可能就得改调度了,涉及到任务抢占/饥饿问题,可能麻烦一点

2 你说的也对可能会堆积,但是epoll任务是比较少的

如果改了优先级,会存在你说的优先级高的任务太多导致其他任务饿死

@wanghenshui 是的,当前的改动是特化的,因为目前我只分析出了 epoll 这种任务同优先级带来的问题,所以就只修改了 epoll。 我们是可以增加一个 priority queue,这样可以对使用 bthread 的人开发增加更多的可能性,但是还需要完善,像你说的,随意使用 priority queue 可能会带来饥饿问题,在这个策略没想好之前,可以通过限制 queue 的大小,来一定程度保证不会饥饿,或者不开放这个接口,是 private 的,只作为内部使用。

yanglimingcn commented 23 hours ago

能否在bthread这个层面上,创建协程的时候设置一个属性,使它具有更高的优先级呢?现在这里专门设置了epoll_XXX,感觉有点特化。 另外,总是让epoll的bthread优先调度的话,会不会造成堆积很多任务的情况呢?

@yanglimingcn 我觉得可以增加一个属性来表示高优先级,但是要说明一点,就是 epoll 的 任务被 steal 的前提肯定是有 worker 需要 steal from task_control,所以可证明 worker 自己的 queue 应该没有任务,证明他是空闲的,这个时候应该优先让其响应网络事件,这有两个好处

  1. 保证网络任务能够及时响应。
  2. 可以让所有的 worker 的 run_q 相对均匀,因为 q 中大量任务来源不是 steal,而是 deal event。 另一个问题,就是是否会造成堆积: 如果我们不响应 epoll 就会造成堆积,证明此刻的网络请求数量是大于 brpc 的处理能力的,否则我们不需要通过增加 steal 的次数这种比起 pop queue 更费时间的操作来加快某时刻处理存量 bthread 的能力。这是牺牲了 RT 带来的,感觉不合理。而且 QPS 高时候更容易造成 RT 抖动。
// Find next task to run, if none, switch to idle thread of the group.

ifndef BTHREAD_FAIR_WSQ

const bool popped = g->_rq.pop(&next_tid);

else

const bool popped = g->_rq.steal(&next_tid);

endif

我看这块的代码是把任务的调度反转了,能达到先执行epoll的目的?