golang GC分析?runtime.mallocgc似乎是最上面的一个;然后移动到sync.Pool解决方案?
我有一个用Go编写的应用程序正在进行消息处理,需要以20K/秒(可能更多)的速率从网络(UDP)接收消息,并且每条消息可以达到UDP数据包的最大长度(64KB headersize),程序需要解码该传入数据包并编码为另一种格式并发送到另一个网络 现在,在24core+64GB RAM机器上,它运行正常,但偶尔会丢失一些数据包,编程模式已经遵循使用多个go例程/通道的原则,它占用了整个机器cpu负载的10%;因此,它有可能使用更多的CPU%或RAM来处理所有20K/s的消息,而不会丢失任何消息;然后我开始分析,我在cpu分析文件中找到了运行时golang GC分析?runtime.mallocgc似乎是最上面的一个;然后移动到sync.Pool解决方案?,go,Go,我有一个用Go编写的应用程序正在进行消息处理,需要以20K/秒(可能更多)的速率从网络(UDP)接收消息,并且每条消息可以达到UDP数据包的最大长度(64KB headersize),程序需要解码该传入数据包并编码为另一种格式并发送到另一个网络 现在,在24core+64GB RAM机器上,它运行正常,但偶尔会丢失一些数据包,编程模式已经遵循使用多个go例程/通道的原则,它占用了整个机器cpu负载的10%;因此,它有可能使用更多的CPU%或RAM来处理所有20K/s的消息,而不会丢失任何消息;然
。mallocgc
出现在顶部,即垃圾收集器运行时,我怀疑此GC可能是罪魁祸首它挂起几毫秒(或几微秒)并丢失了一些数据包,一些最佳实践表明切换到同步。池可能会有所帮助,但我切换到池似乎会导致更多的CPU争用,丢失更多的数据包,而且丢失的频率更高
(pprof) top20 -cum (sync|runtime)
245.99s of 458.81s total (53.61%)
Dropped 487 nodes (cum <= 22.94s)
Showing top 20 nodes out of 22 (cum >= 30.46s)
flat flat% sum% cum cum%
0 0% 0% 440.88s 96.09% runtime.goexit
1.91s 0.42% 1.75% 244.87s 53.37% sync.(*Pool).Get
64.42s 14.04% 15.79% 221.57s 48.29% sync.(*Pool).getSlow
94.29s 20.55% 36.56% 125.53s 27.36% sync.(*Mutex).Lock
1.62s 0.35% 36.91% 72.85s 15.88% runtime.systemstack
22.43s 4.89% 41.80% 60.81s 13.25% runtime.mallocgc
22.88s 4.99% 46.79% 51.75s 11.28% runtime.scanobject
1.78s 0.39% 47.17% 49.15s 10.71% runtime.newobject
26.72s 5.82% 53.00% 39.09s 8.52% sync.(*Mutex).Unlock
0.76s 0.17% 53.16% 33.74s 7.35% runtime.gcDrain
0 0% 53.16% 33.70s 7.35% runtime.gcBgMarkWorker
0 0% 53.16% 33.69s 7.34% runtime.gcBgMarkWorker.func2
不确定我做错了吗?
或者想知道还有什么其他方法可以调整GC以减少CPU%的使用?go版本是1.8
列表显示池中发生了大量锁争用。getSlow
(pprof)列表同步。*.getSlow
总计:7.65分钟
例程======================================同步。(*Pool).getSlow in/opt/go1.8/src/sync/Pool.go
1.07分钟3.69分钟(平房、双人房)占总数的48.29%
. . 144:x=p.New()
. . 145: }
. . 146:返回x
. . 147:}
. . 148:
80ms 80ms 149:func(p*Pool)getSlow()(x接口{}){
..150://参见pin中关于荷载顺序的注释。
30ms 30ms 151:size:=atomic.loaduintpttr(&p.localSize)//加载获取
180ms 180ms 152:local:=p.local//加载消耗
..153://尝试从其他进程中窃取一个元素。
30ms 130ms 154:pid:=运行时_procPin()
20ms 20ms 155:runtime_procumpin()
730ms 730ms 156:对于i:=0;i=0{
..161:x=l.共享[最后]
…162:l.shared=l.shared[:last]
.10ms 163:l.解锁()
…164:休息
. . 165: }
490ms 37.59s 166:l.解锁()
. . 167: }
40ms 40ms 168:返回x
. . 169:}
. . 170:
. . 171://pin将当前goroutine固定到P,禁用抢占并返回P的poolLocal池。
. . 172://调用方在处理池时必须调用运行时_procumpin()。
. . 173:func(p*Pool)pin()*poolLocal{
作为短期对象的一部分维护的自由列表不是
适用于水池,因为间接费用在池中不能很好地摊销
实现这样的对象更有效
他们自己的免费名单
sync.Pool运行速度慢,并发负载高。尝试在启动期间分配所有结构一次,并多次使用。例如,您可以在启动时创建多个goroutine(worker),而不是在每个请求上运行新的goroutine。我建议阅读这篇文章:。Go 1.13(2019年第4季度)可能会改变这一点:请参阅 最初的问题是:“同步:避免在每个GC上清除完整池” 我发现每个周期大约有1000次分配,这让我感到惊讶。这似乎表明池在每次GC时都在清除其全部内容。
A似乎表明了这一点 结果:
sync
:使用受害者缓存平滑GC上的池行为
目前,每个池在每次GC开始时都会被完全清除。这对于池的大量用户来说是一个问题,因为它会在池清除后立即导致分配峰值,从而影响吞吐量和延迟 此CL通过引入受害者缓存机制来解决此问题 不再清除池,而是删除受害者缓存,并删除主缓存 已移动到受害者缓存 因此,在稳定状态下,(大致上)没有新的分配,但如果池使用率下降,对象仍将在两个GC(而不是一个)内收集 这种受害者缓存方法还提高了池对GC动态的影响。
当前的方法会导致池中的所有对象都是短期的。但是,如果应用程序处于稳定状态,并且只是要重新填充其池,则这些对象会像长期存在一样影响实时堆大小。
由于池对象在计算GC触发器和目标时被视为短期对象,但在活动堆中充当长期对象,这会导致GC过于频繁地触发。
如果池对象是应用程序堆的重要部分,则 增加GC的CPU开销。受害者缓存允许池对象 作为长寿命对象影响GC触发器和目标 这对
Get
/Put
性能没有影响,但是。PoolExpensiveNew
通过大幅降低
“New
”功能
// create this one globally at program init
var rfpool = &sync.Pool{New: func() interface{} { return new(aPrivateStruct); }}
// get
rf := rfpool.Get().(*aPrivateStruct)
// put after done processing this message
rfpool.Put(rf)
(pprof) list sync.*.getSlow
Total: 7.65mins
ROUTINE ======================== sync.(*Pool).getSlow in /opt/go1.8/src/sync/pool.go
1.07mins 3.69mins (flat, cum) 48.29% of Total
. . 144: x = p.New()
. . 145: }
. . 146: return x
. . 147:}
. . 148:
80ms 80ms 149:func (p *Pool) getSlow() (x interface{}) {
. . 150: // See the comment in pin regarding ordering of the loads.
30ms 30ms 151: size := atomic.LoadUintptr(&p.localSize) // load-acquire
180ms 180ms 152: local := p.local // load-consume
. . 153: // Try to steal one element from other procs.
30ms 130ms 154: pid := runtime_procPin()
20ms 20ms 155: runtime_procUnpin()
730ms 730ms 156: for i := 0; i < int(size); i++ {
51.55s 51.55s 157: l := indexLocal(local, (pid+i+1)%int(size))
580ms 2.01mins 158: l.Lock()
10.65s 10.65s 159: last := len(l.shared) - 1
40ms 40ms 160: if last >= 0 {
. . 161: x = l.shared[last]
. . 162: l.shared = l.shared[:last]
. 10ms 163: l.Unlock()
. . 164: break
. . 165: }
490ms 37.59s 166: l.Unlock()
. . 167: }
40ms 40ms 168: return x
. . 169:}
. . 170:
. . 171:// pin pins the current goroutine to P, disables preemption and returns poolLocal pool for the P.
. . 172:// Caller must call runtime_procUnpin() when done with the pool.
. . 173:func (p *Pool) pin() *poolLocal {