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

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

Open
someview opened this issue Jan 3, 2024 · 7 comments
Assignees
Labels
enhancement New feature or request needs more info proposal Proposal for this repo waiting for response waiting for the response from commenter

Comments

@someview
Copy link

someview commented Jan 3, 2024

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.

@someview someview added enhancement New feature or request proposal Proposal for this repo labels Jan 3, 2024
@panjf2000
Copy link
Owner

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

@panjf2000 panjf2000 added waiting for response waiting for the response from commenter needs more info labels Jan 4, 2024
@someview
Copy link
Author

someview commented Jan 4, 2024

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

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

@panjf2000
Copy link
Owner

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

@someview
Copy link
Author

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

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

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

@lesismal
Copy link

异步库有太多事先不确定长度的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
Copy link
Author

异步库有太多事先不确定长度的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
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request needs more info proposal Proposal for this repo waiting for response waiting for the response from commenter
Projects
None yet
Development

No branches or pull requests

3 participants