文档章节

Go: 理解Sync.Pool的设计思想

软件破解
 软件破解
发布于 12/03 19:04
字数 1447
阅读 14
收藏 0
Go

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

Sync包提供了强大的可被重复利用实例池,为了降低垃圾回收的压力。在使用这个包之前,需要将你的应用跑出使用pool之前与之后的benchmark数据,因为在一些情况下使用如果你不清楚pool内部原理的话,反而会让应用的性能下降。 pool的局限性 我们先来看看一些基础的例子,来看看他在一个相当简单情况下(分配1K内存)是如何工作的: type Small struct { a int }

var pool = sync.Pool{ New: func() interface{} { return new(Small) }, }

//go:noinline func inc(s *Small) { s.a++ }

func BenchmarkWithoutPool(b *testing.B) { var s *Small for i := 0; i < b.N; i++ { for j := 0; j < 10000; j++ { s = &Small{ a: 1, } b.StopTimer(); inc(s); b.StartTimer() } } }

func BenchmarkWithPool(b *testing.B) { var s *Small for i := 0; i < b.N; i++ { for j := 0; j < 10000; j++ { s = pool.Get().(*Small) s.a = 1 b.StopTimer(); inc(s); b.StartTimer() pool.Put(s) } } } 复制代码下面是两个benchmarks,一个是使用了sync.pool一个没有使用 name time/op alloc/op allocs/op WithoutPool-8 3.02ms ± 1% 160kB ± 0% 1.05kB ± 1% WithPool-8 1.36ms ± 6% 1.05kB ± 0% 3.00 ± 0% 复制代码由于这个遍历有10k的迭代,那个没有使用pool的benchmark显示在堆上创建了10k的内存分配,而使用了pool的只使用了3. 3个分配由pool进行的,但只有一个结构体的实例被分配到内存。到目前为止可以看到使用pool对于内存的处理以及内存消耗上面更加友善。 但是,在实际例子里面,当你使用pool,你的应用将会有很多新在堆上的内存分配。这种情况下,当内存升高了,就会触发垃圾回收。 我们可以强制垃圾回收的发生通过使用runtime.GC()来模拟这种情形 name time/op alloc/op allocs/op WithoutPool-8 993ms ± 1% 249kB ± 2% 10.9k ± 0% WithPool-8 1.03s ± 4% 10.6MB ± 0% 31.0k ± 0% 复制代码我们现在可以看到使用了pool的情况反而内存分配比不使用pool的时候高了。我们来深入地看一下这个包的源码来理解为什么会这样。 内部工作流 看一下sync/pool.go文件会给我们展示一个初始化函数,这个函数里面的内容能解释我们刚刚的情景: func init() { runtime_registerPoolCleanup(poolCleanup) } 复制代码这里在运行时注册成了一个方法去清理pools。并且同样的方法在垃圾回收里面也会触发,在文件runtime/mgc.go里面 func gcStart(trigger gcTrigger) { [...] // clearpools before we start the GC clearpools() 复制代码这就解释了为什么当调用垃圾回收时,性能会下降。pools在每次垃圾回收启动时都会被清理。这个文档其实已经有警告我们 Any item stored in the Pool may be removed automatically at any time without notification 复制代码接下来让我们创建一个工作流来理解一下这里面是如何管理的

sync.Pool workflow in Go 1.12

我们创建的每一个sync.Pool,go都会生成一个内部池poolLocal连接着各个processer(GMP中的P)。这些内部的池由两个属性组成private和shared。前者只是他的所有者可以访问(push以及pop操作,也因此不需要锁),而`shared可以被任何processer读取并且是需要自己维持并发安全。而实际上,pool不是一个简单的本地缓存,他有可能在我们的程序中被用于任何的协程或者goroutines Go的1.13版将改善对shared的访问,还将带来一个新的缓存,该缓存解决与垃圾回收器和清除池有关的问题。 新的无需锁pool和victim cache Go 1.13版本使用了一个新的双向链表作为shared pool,去除了锁,提高了shared的访问效率。这个改造主要是为了提高缓存性能。这里是一个访问shared的流程 https://www.jianshu.com/p/e00f7ebc5a8c https://www.jianshu.com/p/2d3124f51a3d https://www.jianshu.com/p/e2953bb0b576 https://www.jianshu.com/p/80c6a4f5436a https://www.jianshu.com/p/e19641efc173

new shared pools in Go 1.13

在这个新的链式pool里面,每一个processpr都可以在链表的头进行push与pop,然后访问shared可以从链表的尾pop出子块。结构体的大小在扩容的时候会变成原来的两倍,然后结构体之间使用next/prev指针进行连接。结构体默认大小是可以放下8个子项。这意味着第二个结构体可以容纳16个子项,第三个是32个子项以此类推。同样地,我们现在不再需要锁,代码执行具有原子性。 关于新缓存,新策略非常简单。 现在有2组池:活动池和已归档池。 当垃圾收集器运行时,它将保留每个池对该池内新属性的引用,然后在清理当前池之前将池的集合复制到归档池中: // Drop victim caches from all pools. for _, p := range oldPools { p.victim = nil p.victimSize = 0 }

// Move primary cache to victim cache. for _, p := range allPools { p.victim = p.local p.victimSize = p.localSize p.local = nil p.localSize = 0 }

// The pools with non-empty primary caches now have non-empty // victim caches and no pools have primary caches. oldPools, allPools = allPools, nil 复制代码通过这种策略,由于受害者缓存,该应用程序现在将有一个更多的垃圾收集器周期来创建/收集带有备份的新项目。 在工作流中,将在共享池之后在过程结束时请求牺牲者缓存。

© 著作权归作者所有

软件破解
粉丝 0
博文 9
码字总数 19787
作品 0
郑州
私信 提问
同步之sync.Pool临时对象池

同步之sync.Pool临时对象池 当多个goroutine都需要创建同一个对象的时候,如果goroutine过多,可能导致对象的创建数目剧增。 而对象又是占用内存的,进而导致的就是内存回收的GC压力徒增。造...

秋风醉了
2016/08/05
659
0
从Go高性能日志库zap看如何实现高性能Go组件

直接看原文原文地址 摘要日志在整个工程实践中的重要性不言而喻,在选择日志组件的时候也有多方面的考量。详细、正确和及时的反馈是必不可少的,但是整个性能表现是否也是必要考虑的点呢?美...

solate
10/21
4
0
请问sync.Pool有什么缺点?

1.12及之前版本的sync.Pool有三个问题: 每次GC都回收所有对象,如果缓存对象数量太大,会导致STW1阶段的耗时增加。 每次GC都回收所有对象,导致缓存对象命中率下降,New方法的执行造成额外的...

木白的技术私厨
08/07
0
0
深度 | 从Go高性能日志库zap看如何实现高性能Go组件

导语:zap是uber开源的Go高性能日志库。本文作者深入分析了zap的架构设计和具体实现,揭示了zap高效的原因。并且对如何构建高性能Go语言库给出自己的建议。 作者简介:李子昂,美图公司架构平...

高可用架构
2018/08/15
0
0
[转载]golang sync.Pool

Go 1.3 的sync包中加入一个新特性:Pool。 官方文档可以看这里http://golang.org/pkg/sync/#Pool 这个类设计的目的是用来保存和复用临时对象,以减少内存分配,降低CG压力。 Get返回Pool中的...

大蓝妹
2014/10/29
152
0

没有更多内容

加载失败,请刷新页面

加载更多

CrashReport

CrashReport.initCrashReport(getApplicationContext(), buglyID, false); https://bugly.qq.com/v2/ https://blog.csdn.net/Crystal_xing/article/details/86249373......

shzwork
10分钟前
2
0
OSChina 周日乱弹 —— 吃这个吮指原味小松鼠

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @这次装个文艺青年吧 :#今日歌曲推荐# 分享Sam Jonsson/Mattafix的单曲《Big City Life (Sam Jonsson Remix)》: 《Big City Life (Sam Jons...

小小编辑
今天
8
0
使用Docker部署第一个Springboot项目

创建springboot项目后pom文件添加 <packaging>jar</packaging> 双击package打包。 双击package即可,最后只要等待控制台输出SUCCESS即可。 我们会在项目中的target文件夹中到自己打包的jar。...

Ryub
今天
7
0
Spring Boot 中使用@DateTimeFormat和@JsonFormat注解

import com.fasterxml.jackson.annotation.JsonFormat;import lombok.Data;import lombok.experimental.Accessors;import org.springframework.format.annotation.DateTimeFormat;......

不再熬夜
昨天
6
0
Qt编写图片及视频TCP/UDP网络传输

一、前言 很多年前就做过类似的项目,无非就是将本地的图片上传到服务器,就这么简单,其实用http的post上传比较简单容易,无需自定义协议,直接设置好二进制数据即可,而采用TCP或者UDP通信...

飞扬青云
昨天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部