panjf2000 / gnet

🚀 gnet is a high-performance, lightweight, non-blocking, event-driven networking framework written in pure Go.
https://gnet.host
Apache License 2.0
9.62k stars 1.04k forks source link

[Feature]: 添加pool来管理asyncwritev的内存, 以及客户端的api是否可以独立于服务端 #521

Closed someview closed 3 weeks ago

someview commented 9 months ago

Description of new feature

  1. 当前gnet的客户端使用的是gnet.client+ eventhandler的形式来创建连接,这对于连接重连这样的逻辑不是很友好
  2. asyncwritev这样传输一个[][]byte, 这里是否可以增加内存管理的功能呢

Scenarios for new feature

需要实现一个高效的sub-pub message system. 本来是异步的方案,采用gnet比采用netpoll好一些,netpoll本身提供了相当于[][]byte级别的内存管理。另外,感觉gnet的客户端库是否可以和服务端不一样呢

Breaking changes or not?

Yes

Code snippets (optional)

No response

Alternatives for new feature

None.

Additional context (optional)

None.

panjf2000 commented 9 months ago

这里的内存管理是指什么?能具体说一下吗?

someview commented 9 months ago

这里的内存管理是指什么?能具体说一下吗?

基于gnet去实现框架的批量发送功能时,如果考虑内存复用,等特性,就需要自己去管理pool. 类似于netpoll的mux的writequeue,相当于gnet也有自己的writequeue了.

panjf2000 commented 9 months ago

sync.Pool 就能实现一个简单的内存池了,结合 asyncwritev 的回调函数可以方便地回收内存到 pool,自己管理这部分在我看来也不麻烦啊,还是说你还想要其他的东西?能不能用(伪)代码具体表示一下你的确切需求?

someview commented 9 months ago

sync.Pool 就能实现一个简单的内存池了,结合 asyncwritev 的回调函数可以方便地回收内存到 pool,自己管理这部分在我看来也不麻烦啊,还是说你还想要其他的东西?能不能用(伪)代码具体表示一下你的确切需求?

起因是希望能实现oneway形式的通讯模式的sub-pub system,需要提升单个tcp连接上的传输性能。 使用writev系统调用来传输数据是个很好的选择.但是内存管理比较麻烦。类似于并发安全的writequeue buffer, 可以对接writev系统调用。

这种内存管理有两个用途,一个是单个tcp连接上的多路复用,另外一个是应用级别的内存管理

lesismal commented 9 months ago

异步库有太多事先不确定长度的buffer及append需求,append后size就不对齐了并且大于get时候的那个level的pool,所以常见的分级size的池很容易出现 small size pool 取出、放回 big size pool,这样就效果更差。

如果不分级、只用一个pool,同样存在append的问题,下次取出来没法保证size够用,可能会导致pool内的buffer都向大size看齐。

基于标准库同步接口的Conn、同步编解码逻辑的框架,不涉及这个问题,pool的用法也相对容易的多,异步库非常麻烦。

我的方案是默认使用单个pool在通常场景下够用,比较特殊的时候不使用pool。 高版本go支持debug.SetMemoryLimit后,相对好用的多,不太需要自己去判断并手动GC了,这两种都可以配合debug.SetMemoryLimit来尽量及时回收内存来保障程序的稳定。 在做ws百万连接测试中有尝试用debug.SetMemoryLimit,效果确实还不错,不一定能提升响应性能,但能让程序尽量稳定,推荐使用。

另外一种方案是用cgo去搞,一些团队有这样做、用cgo去调用jemalloc那些,效果挺不错的。但cgo又需要精确控制malloc/free一对一的调用,跟arena草案用法类似,都引入了更多的复杂度和心智负担。因为我自己的一些项目实现里没有保障一对一的malloc/free,所以用这种有风险。为了避免泄漏,又弄了个利用SetFinalizer在[]byte变量GC时自动cgo free的玩具,但GC及时性仍然是不够好的,如果配合手动GC能确保及时性其实也还好、但非必要不想增加手动的额外开销,所以暂时没有在生产中使用过这个

someview commented 9 months ago

异步库有太多事先不确定长度的buffer及append需求,append后size就不对齐了并且大于get时候的那个level的pool,所以常见的分级size的池很容易出现 small size pool 取出、放回 big size pool,这样就效果更差。

如果不分级、只用一个pool,同样存在append的问题,下次取出来没法保证size够用,可能会导致pool内的buffer都向大size看齐。

基于标准库同步接口的Conn、同步编解码逻辑的框架,不涉及这个问题,pool的用法也相对容易的多,异步库非常麻烦。

我的方案是默认使用单个pool在通常场景下够用,比较特殊的时候不使用pool。 高版本go支持debug.SetMemoryLimit后,相对好用的多,不太需要自己去判断并手动GC了,这两种都可以配合debug.SetMemoryLimit来尽量及时回收内存来保障程序的稳定。 在做ws百万连接测试中有尝试用debug.SetMemoryLimit,效果确实还不错,不一定能提升响应性能,但能让程序尽量稳定,推荐使用。

另外一种方案是用cgo去搞,一些团队有这样做、用cgo去调用jemalloc那些,效果挺不错的。但cgo又需要精确控制malloc/free一对一的调用,跟arena草案用法类似,都引入了更多的复杂度和心智负担。因为我自己的一些项目实现里没有保障一对一的malloc/free,所以用这种有风险。为了避免泄漏,又弄了个利用SetFinalizer在[]byte变量GC时自动cgo free的玩具,但GC及时性仍然是不够好的,如果配合手动GC能确保及时性其实也还好、但非必要不想增加手动的额外开销,所以暂时没有在生产中使用过这个

类似于netpoll的linkedbuffer确实是实践出来的参数,什么情况下的buffer node需要重新分配,什么样的情况下node可以直接使用,确实很麻烦。

lesismal commented 9 months ago

linkedbuffer是不是最优我建议还是要自己实际测试、实践来看看,不能别人说啥就信啥的。。。

github-actions[bot] commented 1 month ago

This issue is marked as stale because it has been open for 30 days with no activity.

You should take one of the following actions:

This issue will be automatically closed in 7 days if no further activity occurs.

github-actions[bot] commented 3 weeks ago

This issue was closed because it has been inactive for 7 days since being marked as stale.

If you believe this is a false alarm, please leave a comment for it or open a new issue, you can also reopen this issue directly if you have permission.