Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

non-blocking write性能、消耗、时序 #3

Closed
lesismal opened this issue Feb 25, 2023 · 220 comments
Closed

non-blocking write性能、消耗、时序 #3

lesismal opened this issue Feb 25, 2023 · 220 comments

Comments

@lesismal
Copy link
Contributor

lesismal commented Feb 25, 2023

简单看了下,每个conn上自带一个size为8的workerQueue,但是这样似乎有几个问题:

  1. doWrite中是有锁的,所以即使多个协程同时写,也是在锁这里排队,而且还加剧了锁的竞争自旋、可能cpu消耗更高了,所以这里的多协程异步写目测并不能提高性能
  2. 高并发时协程数量、资源消耗更多
  3. 这个workerQueue并发执行时,好像并不能保证发送队列中的数据按照WriteAsync时入队列的顺序。
    而单个conn的消息,很多时候是需要时序保证的。比如流媒体推流需要广播不能允许被单个conn卡其他conn,所以就得WriteAsync,但是推送的数据顺序错乱了音视频就乱码了丢帧了。比如游戏一些玩家操作顺序广播出去,本来是ABC,用户收到变成了CAB。。。

兄弟我建议这块就按常规简单的方式来,单个协程+limited size chan+select default/timeout循环发送就可以了,继续这样下去好像越走越远了。。。

@lesismal lesismal changed the title non-blocking write时序 non-blocking write性能、消耗、时序 Feb 25, 2023
@lesismal
Copy link
Contributor Author

基于标准库阻塞io的websocket,melody虽然也有些浪费的地方,但它在gorilla基础上的封装方式是做得比较不错的,可以参考下:
https://github.com/olahol/melody

@lxzan
Copy link
Owner

lxzan commented Feb 25, 2023

async read/write都是可选的,对时序有要求可以采用同步模式. 它们都通过了autobahn的测试,相比同步模式多了一些not strict.

@lxzan
Copy link
Owner

lxzan commented Feb 25, 2023

事实上,我们跑benchmark才会出现单连接高并发的情况,实际业务锁竞争不会特别激烈.

@lxzan
Copy link
Owner

lxzan commented Feb 25, 2023

基于标准库阻塞io的websocket,melody虽然也有些浪费的地方,但它在gorilla基础上的封装方式是做得比较不错的,可以参考下: https://github.com/olahol/melody

下午去看看借鉴下

@lesismal
Copy link
Contributor Author

async read/write都是可选的,对时序有要求可以采用同步模式. 它们都通过了autobahn的测试,相比同步模式多了一些not strict.
事实上,我们跑benchmark才会出现单连接高并发的情况,实际业务锁竞争不会特别激烈.

这两条的前提都是基于运气好。业务量再小,只要时间足够,也会遇到问题——墨菲定律了解一下。

如果基础设施的代码基于运气,谁敢用啊兄弟,:joy:

@lesismal
Copy link
Contributor Author

时序的问题,如果你想节省协程也可以做到,消息入队列如果是队首则启动go后for循环去发送队列里的数据,这样也能做到没有待发送的数据时不用占用协程,类似我这里:
https://github.com/lesismal/nbio/blob/master/conn.go#L103

你现在的workerqueue size设置为1、do的时候改造下判断是否队首应该就可以。目前没有判断队首直接改size为1应该是不行的,并发AddJob会乱

@lxzan
Copy link
Owner

lxzan commented Feb 25, 2023

async read/write都是可选的,对时序有要求可以采用同步模式. 它们都通过了autobahn的测试,相比同步模式多了一些not strict.
事实上,我们跑benchmark才会出现单连接高并发的情况,实际业务锁竞争不会特别激烈.

这两条的前提都是基于运气好。业务量再小,只要时间足够,也会遇到问题——墨菲定律了解一下。

如果基础设施的代码基于运气,谁敢用啊兄弟,😂

并不是运气. 对于线上环境, 单连接内出现高并发, 十有八九是DDOS. 而且, 我这套基于任务队列的IO模型是有改进空间的. channel并发写之所以高效, 是因为并行写入内存(加锁解锁时间很短), 串行写入net.Conn.

@lxzan
Copy link
Owner

lxzan commented Feb 25, 2023

时序的问题,如果你想节省协程也可以做到,消息入队列如果是队首则启动go后for循环去发送队列里的数据,这样也能做到没有待发送的数据时不用占用协程,类似我这里: https://github.com/lesismal/nbio/blob/master/conn.go#L103

你现在的workerqueue size设置为1、do的时候改造下判断是否队首应该就可以。目前没有判断队首直接改size为1应该是不行的,并发AddJob会乱

被你发现了, 我就是不想增加常驻协程.

@lesismal
Copy link
Contributor Author

1.14后,go的调度是抢占式的了,即使纯cpu消耗的代码也可能被中途打断的。所以这并不是你的并发量有多大的问题,只要发送队列大于等于2,基于墨菲定律,就肯定会发生。
这还不是运气式啥!?清醒一点

@lxzan
Copy link
Owner

lxzan commented Feb 25, 2023

1.14后,go的调度是抢占式的了,即使纯cpu消耗的代码也可能被中途打断的。所以这并不是你的并发量有多大的问题,只要发送队列大于等于2,基于墨菲定律,就肯定会发生。 这还不是运气式啥!?清醒一点

就墨菲定律来说, 你无法100%保证所有用户的QoS, 就像机械效率无法达到100%.

@lesismal
Copy link
Contributor Author

被你发现了, 我就是不想增加常驻协程.

nbio里使用的默认协程池没任务的时候就是退出的不占用资源的,但是基于自然均衡的需求,留了一个带size chan当队列的常驻协程,一个协程成本无所谓。

@lesismal
Copy link
Contributor Author

就墨菲定律来说, 你无法100%保证所有用户的QoS, 就像机械效率无法达到100%.

计算机科学的分层思想很有道理。就可靠性来讲,我觉得:

  1. 每一层应该尽量保障自己这一层的可用性;
  2. 从硬件到软件协议栈,每一层都有校验和、可靠协议校验失败丢弃重发之类的机制,都是尽量确保自己这一层的稳定性可用性;
  3. 如果每一层都觉得其他层无法100%所以自己也基于运气,那整个系统的可靠性就更低了

而且这个问题,完全可以做到更好的可靠性代码+小于等于当前方案的消耗+更低的理论消耗上限

@lesismal
Copy link
Contributor Author

这个workerqueue设计得挺好,但更适合用于通用协程池的场景。单个conn这种有时序要求的,再特化一下会更好

@lesismal
Copy link
Contributor Author

字节的那个workerpool本质也是类似的,细节差异罢了

@lxzan
Copy link
Owner

lxzan commented Feb 25, 2023

这个workerqueue设计得挺好,但更适合用于通用协程池的场景。单个conn这种有时序要求的,再特化一下会更好

再加一个有锁队列(RWMutex),可以达到channel的效果

@lxzan
Copy link
Owner

lxzan commented Feb 25, 2023

这个workerqueue设计得挺好,但更适合用于通用协程池的场景。单个conn这种有时序要求的,再特化一下会更好

从我的concurrency库复制过来的. 看过字节和ants两个库,都没看明白原理😂

@lesismal
Copy link
Contributor Author

lesismal commented Feb 25, 2023

字节的主要是自己的简单链表+pool略有优化,用的for循环,你的是数组+递归,本质上你俩这个是相同的。

ants写的太复杂,我没深入读过它代码,简单扫了下应该是用的条件变量,类似c/cpp那套。因为写的太复杂,而且我看issue里有人遇到一些莫名其妙的bug,而且较高版本go里我benchmark了下它更慢,所以就更没打算看它源码了。

我那个协程池为了减少一个协程池只有单个数组或者链表时锁操作的竞争浪费,保留了一个常驻协程用chan做队列,当前并发度没有达到协程数量限制时则只要一个原子操作,比无竞争锁需要加锁解锁省掉一些原子操作,比竞争时try lock部分自旋节省更多,以一个常驻协程代价换更均衡的。对于单个conn做了上面说的那个时序保证、但这是conn自己的部分、并不是协程池本身的功能。我这里单独实现了一个有序化的,相当于把conn上那个拆出来了,可以配合任何协程池使用:
https://github.com/lesismal/serial

@lesismal
Copy link
Contributor Author

再加一个有锁队列(RWMutex),可以达到channel的效果

应该是不需要用RWMutex的,这种队列+线程/协程池,通常是需要push/pop的原子性保障,读锁并发时没法保证push/pop的原子性,所以可能会带来bug。
如果只使用Lock/Unlock,则Mutex性能好于RWMutex,看下源码就懂了。lockSlow那个内联的注释让人心情非常愉悦:
https://github.com/golang/go/blob/master/src/sync/mutex.go#L89

@lxzan
Copy link
Owner

lxzan commented Feb 25, 2023

字节的主要是自己的简单链表+pool略有优化,用的for循环,你的是数组+递归,本质上你俩这个是相同的。

ants写的太复杂,我没深入读过它代码,简单扫了下应该是用的条件变量,类似c/cpp那套。因为写的太复杂,而且我看issue里有人遇到一些莫名其妙的bug,而且较高版本go里我benchmark了下它更慢,所以就更没打算看它源码了。

我那个协程池为了减少一个协程池只有单个数组或者链表时锁操作的竞争浪费,保留了一个常驻协程用chan做队列,当前并发度没有达到协程数量限制时则只要一个原子操作,比无竞争锁需要加锁解锁省掉一些原子操作,比竞争时try lock部分自旋节省更多,以一个常驻协程代价换更均衡的。对于单个conn做了上面说的那个时序保证、但这是conn自己的部分、并不是协程池本身的功能。我这里单独实现了一个有序化的,相当于把conn上那个拆出来了,可以配合任何协程池使用: https://github.com/lesismal/serial

一个常驻协程无所谓, 连接上面的就可观了

@lesismal
Copy link
Contributor Author

回到问题本身,不管是否优化协程占用,应该严格保证发送顺序、而不是基于运气默认认为不会出现不一致。

@lesismal
Copy link
Contributor Author

lesismal commented Feb 25, 2023

一个常驻协程无所谓, 连接上面的就可观了

我说的一个常驻协程是一个协程池只有一个常驻,一个nbio Engine上的所有conn共用同一个或几个这种协程池,并不是像gws这种一个conn上一个workerpool,所以不存在协程数量可观的问题。
一个conn上只是一个数组作为执行队列、不绑定协程池,而是这个conn解析出一个完整message时,把任务加入到这个conn的执行队列:
https://github.com/lesismal/nbio/blob/master/conn.go#L103
如果这个message的任务就是队首,则用协程池去异步执行并且循环取任务直到执行完所有,这里的c.p.g.Execute是协程池的pool.Go:
https://github.com/lesismal/nbio/blob/master/conn.go#L115
这样就能保证并发入队列的时候单个conn多个任务也只会启动并临时占用一个协程,执行完当前任务列表就退出,并且保证了顺序加入队列的任务的有序执行

@lesismal
Copy link
Contributor Author

对于传统的c/cpp那些框架,我这个时序的方案也是适用的,这样至少可以让多逻辑线程被均衡利用起来。不只是单个conn时序保证,还可以根据模块,每个模块继承/集成一个这种队列+线程池,就能做到逻辑多线程+各种时序保证了

@lxzan
Copy link
Owner

lxzan commented Feb 25, 2023

回到问题本身,不管是否优化协程占用,应该严格保证发送顺序、而不是基于运气默认认为不会出现不一致。

时序问题在v1.3.1已经解决了. 肝了一天, 累死了, 异步IO真是让人头大.

@lesismal
Copy link
Contributor Author

时序问题在v1.3.1已经解决了. 肝了一天, 累死了, 异步IO真是让人头大.

这。。。我简单review了下,应该是没解决的。我展开了说,你看一下是不是这个道理:

1.3.1是每次WriteAsync都先入队列,然后AddJob,每个job执行时是doWriteAsync,但是doWriteAsync是每次先取出所有再循环发送。
比如连续WriteAsync M 个消息,入队列与实际执行写的job并不是一一对应的、不是入队一个写一个的。 而是很可能M个消息分散成了小于M份比如M/2份,被M/2个job分别拿到了去发送,而你一共AddJob了M次,所以还会有M/2个job被异步执行时实际是空跑、浪费了。而且,由于可能同时存在M/2个job在执行,仍然没有保障时序。

今天头大了可以先停一停,休息下散散步喝喝茶,等思路清晰了再改,否则一下子卡住可能越改越乱,我以前也是,好几次肝到凌晨5点甚至早上8点,整个人状态都不好了。。。

@lesismal
Copy link
Contributor Author

这里的85-88行取出所有后解锁了,下面循环发送的过程中,别的地方可能又WriteAsync并触发了新的job异步执行:
https://github.com/lxzan/gws/blob/master/writer.go#L85

这里取所有如果只加锁、等所有发送完再解锁,那其他地方WriteAsync又可能阻塞,所以这里取所有message不管加不加锁都不是正解。我前面说不要使用RWMutex、保证不了原子性也是类似的原因。
要不你先看下我那个conn.Execute或者serial再改:joy:

@lxzan
Copy link
Owner

lxzan commented Feb 25, 2023

时序问题在v1.3.1已经解决了. 肝了一天, 累死了, 异步IO真是让人头大.

这。。。我简单review了下,应该是没解决的。我展开了说,你看一下是不是这个道理:

1.3.1是每次WriteAsync都先入队列,然后AddJob,每个job执行时是doWriteAsync,但是doWriteAsync是每次先取出所有再循环发送。 比如连续WriteAsync M 个消息,入队列与实际执行写的job并不是一一对应的、不是入队一个写一个的。 而是很可能M个消息分散成了小于M份比如M/2份,被M/2个job分别拿到了去发送,而你一共AddJob了M次,所以还会有M/2个job被异步执行时实际是空跑、浪费了。而且,由于可能同时存在M/2个job在执行,仍然没有保障时序。

今天头大了可以先停一停,休息下散散步喝喝茶,等思路清晰了再改,否则一下子卡住可能越改越乱,我以前也是,好几次肝到凌晨5点甚至早上8点,整个人状态都不好了。。。

写入顺序就是入队顺序, 已经跑通了Autobahn测试, 没有再出现额外的几个Non-Strict了. 暂未发现异常, 跑Benchmark性能更强了 😂.

@lesismal
Copy link
Contributor Author

还有一点,nbio的阻塞io的websocket conn的写,是按配置项决定是否开启单独协程负责写的。用户如果不需要广播,不开启就能省点协程。但是如果需要广播、用户配置了异步写,我是用单独的常驻协程处理异步写的,没有做成这种动态协程,因为动态协程性能要差一些,跨协程变量逃逸、调度亲和性都要差。
而且既然用户都使用了阻塞或者混合模式来处理conn而且配置了异步写,应该也不差这点协程数、而是追求性能多一些,并且如果是用混合模式,肯定也是配置了阻塞io的连接数量的,这个数量不会太大,多出这个数量的协程也没压力,其他超过阈值的部分的conn交给poller部分去节省,完全能cover住

@lesismal
Copy link
Contributor Author

写入顺序就是入队顺序, 已经跑通了Autobahn测试, 没有再出现额外的几个Non-Strict了. 暂未发现异常, 跑Benchmark性能更强了

一些问题压测是测不出来的,autobahn也跑通也不代表就没有问题啊。。。
你先看我分析的代码逻辑,是不是确实存在这种可能性

@lxzan
Copy link
Owner

lxzan commented Feb 25, 2023

这里的85-88行取出所有后解锁了,下面循环发送的过程中,别的地方可能又WriteAsync并触发了新的job异步执行: https://github.com/lxzan/gws/blob/master/writer.go#L85

这里取所有如果只加锁、等所有发送完再解锁,那其他地方WriteAsync又可能阻塞,所以这里取所有message不管加不加锁都不是正解。我前面说不要使用RWMutex、保证不了原子性也是类似的原因。 要不你先看下我那个conn.Execute或者serial再改😂

改了再看吧, 今天改休息了

@lxzan
Copy link
Owner

lxzan commented Mar 2, 2023

我是支持配置的. 如果不丢, 任务太多内存都会爆

其实也还好,递归爆栈,在你这个使用场景里应该不太会出现,即使出现了概率也极低。。。 不过如果按我的作风,自己控制的这一层,我是不喜欢放过理论上存在的可能性。。。

实际业务场景会加限流来避免这种情况. 但我还是会加容量限制, 不然心里不舒服

@lesismal
Copy link
Contributor Author

lesismal commented Mar 2, 2023

实际业务场景会加限流来避免这种情况. 但我还是会加容量限制, 不然心里不舒服

用chan的话,就可以自然限流了,非常均衡圆满的感觉,哈哈哈

@lxzan
Copy link
Owner

lxzan commented Mar 2, 2023

不喜欢使用channel, 感觉太重了

@lesismal
Copy link
Contributor Author

lesismal commented Mar 2, 2023

其实chan不重。rpc里每次call都会创建一个,用完就不管了,不close也没什么副作用

@lxzan
Copy link
Owner

lxzan commented Mar 2, 2023

经常需要搭配go for select一起使用, 如果还有多个case, 开销就上来了, 而且代码很丑...

@lesismal
Copy link
Contributor Author

lesismal commented Mar 2, 2023

这可是比手撸cond_t+mux轻松多了。。。
避免不必要的多case就还好,所以我那个整理后的taskpool里只有单个常驻协程是两个case,动态的循环取的时候都是单case+default

@lesismal
Copy link
Contributor Author

lesismal commented Mar 2, 2023

话说我还没试过手撸cond_t+mux方式的协程池,不知道能不能做到比现在这个更快,按道理应该是不能更快,我之前测试ants的性能也没感觉除强来,所以不知道ants为啥那么做:joy:
改天闲了我试试手撸的方式

@lxzan
Copy link
Owner

lxzan commented Mar 2, 2023

gws wq优化之后, ants变成性能最差的了, nbio任务队列性能一骑绝尘

@lxzan
Copy link
Owner

lxzan commented Mar 2, 2023

这玩意根本就不是和线程池类似的概念

@lesismal
Copy link
Contributor Author

lesismal commented Mar 2, 2023

这玩意根本就不是和线程池类似的概念

是啊,线程池还是得各种异步回调,callback hell,而且如果是c/cpp还时刻考虑着内存管理怕泄露,太虐心

@lxzan
Copy link
Owner

lxzan commented Mar 3, 2023

func (c *workerQueue) do(job asyncJob) {
	job()
	if nextJob := c.getJob(-1); nextJob != nil {
		go c.do(nextJob)
	}
}

这里加个go关键字就不会栈溢出了

@lesismal
Copy link
Contributor Author

lesismal commented Mar 3, 2023

func (c *workerQueue) do(job asyncJob) {
	job()
	if nextJob := c.getJob(-1); nextJob != nil {
		go c.do(nextJob)
	}
}

这里加个go关键字就不会栈溢出了

每次新开一个协程,如果真的排队大于1切新协程是性能不划算的,你这么想递归是讳疾忌医:joy:

@lxzan
Copy link
Owner

lxzan commented Mar 3, 2023

两种方式性能相差不大吧, 开了新协程:

const (
	PoolSize   = 100
	BenchTimes = 10000
	N          = 1000
)

goos: darwin
goarch: arm64
pkg: bench
BenchmarkGwsWorkerQueue
BenchmarkGwsWorkerQueue-8   	     295	   3939837 ns/op	  743913 B/op	   24323 allocs/op
BenchmarkGopool
BenchmarkGopool-8           	     328	   4132814 ns/op	  267073 B/op	   15324 allocs/op
BenchmarkAnts
BenchmarkAnts-8             	     288	   3748561 ns/op	  160443 B/op	   10006 allocs/op
BenchmarkNbio
BenchmarkNbio-8             	     463	   2562049 ns/op	  479970 B/op	   29999 allocs/op
PASS
const (
	PoolSize   = 100
	BenchTimes = 1000
	N          = 10000
)

goos: darwin
goarch: arm64
pkg: bench
BenchmarkGwsWorkerQueue
BenchmarkGwsWorkerQueue-8   	    1245	    889013 ns/op	   80871 B/op	    2131 allocs/op
BenchmarkGopool
BenchmarkGopool-8           	     865	   1234968 ns/op	   18864 B/op	    1113 allocs/op
BenchmarkAnts
BenchmarkAnts-8             	    1086	   1087012 ns/op	   16093 B/op	    1001 allocs/op
BenchmarkNbio
BenchmarkNbio-8             	    1401	    845149 ns/op	   48016 B/op	    3000 allocs/op
PASS

@lxzan
Copy link
Owner

lxzan commented Mar 3, 2023

升级版workQueue表现挺不错的, 而且实现方式最简单

const (
	PoolSize   = 100
	BenchTimes = 1000
	N          = 100000
)

goos: darwin
goarch: arm64
pkg: bench
BenchmarkGwsWorkerQueue
BenchmarkGwsWorkerQueue-8   	     198	   6031152 ns/op	   84453 B/op	    2007 allocs/op
BenchmarkGopool
BenchmarkGopool-8           	     100	  10789822 ns/op	   18872 B/op	    1107 allocs/op
BenchmarkAnts
BenchmarkAnts-8             	     195	   6236546 ns/op	   16169 B/op	    1003 allocs/op
BenchmarkNbio
BenchmarkNbio-8             	     169	   6998002 ns/op	   47940 B/op	    2995 allocs/op
PASS

@lesismal
Copy link
Contributor Author

lesismal commented Mar 3, 2023

多换一些参数试试。系统资源、入队和并发度会有阈值。比如poolsize比较大多数时候都新协程,适合实际业务中带有io的任务,因为io阻塞的时候新协程可以继续,避免像我复现的字节那个排队问题。但纯cpu消耗的,并发度再高也没办法提升核心数量对应的计算能力,所以反倒可能是并发度设置的低、多数时候入队等旧的协程执行,避免了新协程的切换消耗。

@lesismal
Copy link
Contributor Author

lesismal commented Mar 3, 2023

不同硬件和测试参数跑出来的数据都不太一样,我自己机器上测,nbio并不是每种参数都最好,但是整体稳定并且适合nbio自带的http和websocket,尤其是http因为肯定会有response的io

@lxzan
Copy link
Owner

lxzan commented Mar 3, 2023

不同于硬件和测试参数跑出来的数据都不太一样,我自己机器上测,nbio并不是每种参数都最好,但是整体稳定并且适合nbio自带的http和websocket,尤其是http因为肯定会有response的io

CPU密集型任务测试结果还算稳定, IO不好测误差大

@lesismal
Copy link
Contributor Author

lesismal commented Mar 3, 2023

我笔记本上一些测试参数+低消耗demofunc,字节的那个跑出来的数据快极了,比直接go还快两倍之类的,我分析可能就是协程切换少,这玩意压测的实际并发竞争调度比较混沌而且测试参数太多,跑profile又是很累,所以我也没profile分析,就是结合源码想象,所以也不知道准确不。要是有时间可以多参数每个框架一轮烤机profile看下:joy:

@lxzan
Copy link
Owner

lxzan commented Mar 3, 2023

感觉任务队列这一块IO优化的空间很小, redis get测试:

goos: darwin
goarch: arm64
pkg: bench
BenchmarkGwsWorkerQueue
BenchmarkGwsWorkerQueue-8   	     100	  11599345 ns/op	  256052 B/op	    9111 allocs/op
BenchmarkGopool
BenchmarkGopool-8           	      93	  11957944 ns/op	  226546 B/op	    9122 allocs/op
BenchmarkAnts
BenchmarkAnts-8             	     100	  11565853 ns/op	  217811 B/op	    9012 allocs/op
BenchmarkNbio
BenchmarkNbio-8             	      86	  12411182 ns/op	  276837 B/op	   11152 allocs/op
PASS

@lesismal
Copy link
Contributor Author

lesismal commented Mar 3, 2023

不是IO优化,是并发度与协程复用率,你多试试不同的poolsize

@lxzan
Copy link
Owner

lxzan commented Mar 3, 2023

不是IO优化,是并发度与协程复用率,你多试试不同的poolsize

从测试结果来看, 很多时候不复用也没什么问题, 相信runtime的调度能力

@lesismal
Copy link
Contributor Author

lesismal commented Mar 3, 2023

不是复用或者不复用有问题,我再解释细点。
首先协程池需要限制协程数量,在这个基础上:

  1. 有阻塞或其他可能导致主动调度的慢任务,应该在未达到协程数量限制前创建新协程来处理task,避免复用遇到少量慢任务导致大家都延迟。这部分慢task阻塞后CPU消耗不高如果其他任务都排队等待,系统资源利用率上不来
  2. 纯cpu消耗的,可以少些并发度尽量复用协程,一个协程处理更多任务减少协程切换,并且节省总协程数量和相关成本

实际场景里2的这种纯cpu消耗类任务比较少见。

不同场景要具体分析,并不是哪种好不好,还得结合实际测试数据针对性调优。

@lxzan
Copy link
Owner

lxzan commented Mar 3, 2023

不是复用或者不复用有问题,我再解释细点。 首先协程池需要限制协程数量,在这个基础上:

  1. 有阻塞或其他可能导致主动调度的慢任务,应该在未达到协程数量限制前创建新协程来处理task,避免复用遇到少量慢任务导致大家都延迟。这部分慢task阻塞后CPU消耗不高如果其他任务都排队等待,系统资源利用率上不来
  2. 纯cpu消耗的,可以少些并发度尽量复用协程,一个协程处理更多任务减少协程切换,并且节省总协程数量和相关成本

实际场景里2的这种纯cpu消耗类任务比较少见。

不同场景要具体分析,并不是哪种好不好,还得结合实际测试数据针对性调优。

很细微的调优了

@lxzan
Copy link
Owner

lxzan commented Mar 3, 2023

感觉我有毒, 老是把API改来改去的. 下次更新变化蛮大的

  1. 改善了任务队列性能
  2. 移除了option模式配置, 新增了io buffer size配置, 规范了各项配置的命名
  3. 修改了CheckOrigin API, 操作session逻辑更加清晰

@lxzan
Copy link
Owner

lxzan commented Mar 3, 2023

希望是最后一次大改吧,感觉是越来越成熟了,提供的配置项越来越多了...

@lesismal
Copy link
Contributor Author

lesismal commented Mar 3, 2023

趁着用户少赶紧改。nbio我总是考虑旧版本兼容性所以还保留着一些初版的内容看着别扭:joy:

@lxzan
Copy link
Owner

lxzan commented Mar 3, 2023

趁着用户少赶紧改。nbio我总是考虑旧版本兼容性所以还保留着一些初版的内容看着别扭😂

大家都有相同的烦恼

@lxzan
Copy link
Owner

lxzan commented Mar 4, 2023

跑了下pprof, 主要都是IO开销, 感觉可优化的空间不大了

@lesismal
Copy link
Contributor Author

lesismal commented Mar 4, 2023

也对,加io的话,其他消耗相比之下就太小了,难分析

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants