go sync: eliminate global Mutex in Pool operations

fcy6dtqo  于 4个月前  发布在  Go
关注(0)|答案(3)|浏览(53)

https://golang.org/cl/101715 的审查中(“regexp:使用sync.Pool缓存regexp.machine对象”),@ianlancetaylor 指出:
https://golang.org/cl/44150043 中,[@bradfitz] 做了类似的更改。但在 https://golang.org/cl/48190043 中将其回滚。[@dvyukov] 说:“剩下的担忧是:是否存在程序创建了许多本地短暂的正则表达式(一次性)的情况,而无法用全局的替换它们?对于这种情况,我们引入了一个全局的争用互斥锁。”
我猜这里的评论指的是 allPoolsMu:
go/src/sync/pool.go
2e84dc2 的第 242 行
| | allPoolsMuMutex |
我觉得 sync.Pool 本不应该有全局的 Mutex。毕竟,sync.Pool 明确与垃圾回收器合作(在 #22950 之后可能会更加合作),而垃圾回收器会在正常垃圾回收过程中追踪所有活跃的 sync.Pool 示例。
allPools 切片似乎存在,以便垃圾回收器可以在追踪它们之前清除所有的 sync.Pool 示例。我们需要在追踪它们之前识别出 Pool,以免过度保留池化对象,但这似乎可以在通常的 GC 安全点之外而不获取全局锁的情况下实现。
例如,我们可以将新分配的池添加到每个G的列表中,并仅在该运行 G 的线程获得调度器锁时将该列表移动到全局列表。
如果我们碰巧错过了当前 GC 周期上的新 Pool(例如,因为其 goroutine 直到循环的末尾才被取消调度),那没关系:我们只需等待下一个 GC 周期来清除它。
这可以使 sync.Pool 对于倾向于长期存在但有时也短暂存在的对象(如编译后的正则表达式)更高效。

umuewwlo

umuewwlo1#

(另请参阅#24411,激励性示例。)

ktecyv1j

ktecyv1j2#

例如,我们可以将新分配的池添加到每个G列表中。
今天我尝试解决这个问题。
我的猜测是每个G列表是一个P本地存储,因此有两种实现方法。
方案1是修改运行时以支持,方案2是通过链接名从运行时获取返回pid的函数,并使用此函数创建P本地存储。
我尝试了方案2,如果你使用map来实现P本地存储,你仍然需要一个锁来同步。你也可以尝试使用切片来实现P本地存储。
最后发现也许可以使用类似于https://go-review.googlesource.com/c/go/+/552515 API的方法,使用两个分片来解决问题。
注意:如果GOMAXPOCS发生变化,分片并不能保证相同的P操作使用相同的内存,不同的Ps操作使用不同的内存。这意味着使用分片只是使锁不再是全局的,使得对相同锁的竞争不再激烈。
这些是我的尝试。
我分享它是为了帮助解决这个问题。

k97glaaz

k97glaaz3#

https://go.dev/cl/562336提到了这个问题:sync: eliminate global Mutex in (*Pool).pinSlow operations

相关问题