golang逃逸分析和竞争检测
最近在线上发现一块代码逻辑在执行N次耗时波动很大1ms~800ms,最开始以为是gc的问题,对代码进行逃逸分析,看哪些变量被分配到堆上了,后来发现是并发编程时对一个切片并发的写,导致存在竞争,类似下面的代码
func main() {
//var count int
array := make([]int, 100000)
wg := new(sync.WaitGroup)
for i := 0; i < 10; i++ {
wg.Add(1)
go func(a []int) {
now := time.Now()
print(a)
fmt.Println("耗时:", time.Since(now))
wg.Done()
}(array)
}
wg.Wait()
}
func print(array []int) {
array[0] = 1
array[1] = 1
for i := 0; i < len(array); i++ {
}
//fmt.Println(array)
}
output:
耗时: 85.532µs
耗时: 49.543µs
耗时: 53.306µs
耗时: 53.365µs
耗时: 47.73µs
耗时: 48.098µs
耗时: 70.815µs
耗时: 71.15µs
耗时: 89.213µs
耗时: 60.797µs
首先试一试逃逸分析:
go build -gcflags '-m -l' main.go
./main.go:27:20: print array does not escape
./main.go:11:15: make([]int, 100000) escapes to heap
./main.go:12:11: new(sync.WaitGroup) escapes to heap
./main.go:15:13: make([]int, 100000) escapes to heap
./main.go:17:6: func literal escapes to heap
./main.go:17:6: func literal escapes to heap
./main.go:20:16: "耗时:" escapes to heap
./main.go:20:37: time.Since(now) escapes to heap
./main.go:21:4: leaking closure reference wg
./main.go:17:15: main.func1 a does not escape
./main.go:20:15: main.func1 ... argument does not escape
结论:切片array由于size太大了被分配到堆上了,字符串"耗时:"被分配到堆上,这里分配到堆上的变量被频繁创建地有newA和字符串"耗时:",newA可以采用变量池sync.Pool解决,字符串应该写成常量形式
耗时47us~89us,很不稳定,对其进行竞争检测
运行命令
go run -race main.go
output:
==================
WARNING: DATA RACE
Write at 0x00c420092000 by goroutine 7:
main.print()
/Users/qianxiaoli/work/mygopath/src/testpprof/bug/main.go:26 +0x49
main.main.func1()
/Users/qianxiaoli/work/mygopath/src/testpprof/bug/main.go:17 +0x8c
Previous write at 0x00c420092000 by goroutine 6:
main.print()
/Users/qianxiaoli/work/mygopath/src/testpprof/bug/main.go:26 +0x49
main.main.func1()
/Users/qianxiaoli/work/mygopath/src/testpprof/bug/main.go:17 +0x8c
Goroutine 7 (running) created at:
main.main()
/Users/qianxiaoli/work/mygopath/src/testpprof/bug/main.go:15 +0xe3
Goroutine 6 (running) created at:
main.main()
/Users/qianxiaoli/work/mygopath/src/testpprof/bug/main.go:15 +0xe3
==================
耗时: 58.625µs
==================
WARNING: DATA RACE
Write at 0x00c420092008 by goroutine 7:
main.print()
/Users/qianxiaoli/work/mygopath/src/testpprof/bug/main.go:27 +0x6d
main.main.func1()
/Users/qianxiaoli/work/mygopath/src/testpprof/bug/main.go:17 +0x8c
Previous write at 0x00c420092008 by goroutine 6:
main.print()
/Users/qianxiaoli/work/mygopath/src/testpprof/bug/main.go:27 +0x6d
main.main.func1()
/Users/qianxiaoli/work/mygopath/src/testpprof/bug/main.go:17 +0x8c
Goroutine 7 (running) created at:
main.main()
/Users/qianxiaoli/work/mygopath/src/testpprof/bug/main.go:15 +0xe3
Goroutine 6 (running) created at:
main.main()
/Users/qianxiaoli/work/mygopath/src/testpprof/bug/main.go:15 +0xe3
==================
结论:
显然并发代码存在竞争,print函数对array并发写操作导致竞争,执行耗时变长。由于切片在拷贝时,底层的数组还是同一个,所以并发写同一个数组会产生竞争。
将代码加入变量池及切片拷贝
var arrayPool = sync.Pool{
New: func() interface{} {
a := make([]int, 2)
return a
},
}
func main() {
array := make([]int, 2)
wg := new(sync.WaitGroup)
for i := 0; i < 10000; i++ {
wg.Add(1)
newA := arrayPool.Get().([]int)
copy(newA, array)
go func(a []int) {
now := time.Now()
print(a)
fmt.Println("耗时:", time.Since(now))
wg.Done()
}(newA)
}
wg.Wait()
}
func print(array []int) {
array[0] = 1
array[1] = 1
for i := 0; i < len(array); i++ {
}
arrayPool.Put(array)
//fmt.Println(array)
}
golang逃逸分析和竞争检测的更多相关文章
- Golang逃逸分析
Golang逃逸分析 介绍逃逸分析的概念,go怎么开启逃逸分析的log. 以下资料来自互联网,有错误之处,请一定告之. sheepbao 2017.06.10 什么是逃逸分析 wiki上的定义 In ...
- 聊聊Golang逃逸分析
逃逸分析的概念,go怎么开启逃逸分析的log. 以下资料来自互联网,有错误之处,请一定告之. 什么是逃逸分析 wiki上的定义 在编译程序优化理论中,逃逸分析是一种确定指针动态范围的方法——分析在程序 ...
- 逃逸分析与栈、堆分配分析 escape_analysis
小结: 1.当形参为 interface 类型时,在编译阶段编译器无法确定其具体的类型.因此会产生逃逸,最终分配到堆上. 2.The construction of a value doesn't d ...
- 基于Golang的逃逸分析(Language Mechanics On Escape Analysis)
何为逃逸分析 在编译程序优化理论中,逃逸分析是一种确定指针动态范围的方法——分析在程序的哪些地方可以访问到指针.它涉及到指针分析和形状分析. 当一个变量(或对象)在子程序中被分配时,一个指向变量的指针 ...
- JVM笔记-逃逸分析
参考: http://www.iteye.com/topic/473355http://blog.sina.com.cn/s/blog_4b6047bc01000avq.html 什么是逃逸分析(Es ...
- Go变量逃逸分析
目录 什么是逃逸分析 为什么要逃逸分析 逃逸分析是怎么完成的 逃逸分析实例 总结 写过C/C++的同学都知道,调用著名的malloc和new函数可以在堆上分配一块内存,这块内存的使用和销毁的责任都在程 ...
- 逃逸分析(Escape Analysis)
一.什么是逃逸 逃逸是指在某个方法之内创建的对象,除了在方法体之内被引用之外,还在方法体之外被其它变量引用到:这样带来的后果是在该方法执行完毕之后,该方法中创建的对象将无法被GC回收,由于其被其它变量 ...
- 深入理解Java中的逃逸分析
在Java的编译体系中,一个Java的源代码文件变成计算机可执行的机器指令的过程中,需要经过两段编译,第一段是把.java文件转换成.class文件.第二段编译是把.class转换成机器指令的过程. ...
- java虚拟机的逃逸分析
逃逸分析作为其他优化手段提供依据的分析技术,其基本行为就是分析对象动态作用域:当一个对象在方法中被定义后,它可能被外部方法所引用,例如作为调用参数传递到其他方法中,称为方法逃逸.甚至还有可能被外部线程 ...
随机推荐
- Golang: 数组和切片
数组 同其他语言一样,数组是一些相同类型的元素的集合.数组声明 数组的类型为 n[T],其中 n 表示数组中元素的个数,T 表示数组中元素的类型.数组元素的个数 n 也是数组类型的一部分 packag ...
- day0-功能自动化的前提
自动化不是万能的,不是什么产品都适合自动化测试,那些产品适合自动化测试? 只要满足下面三个条件就可以进行自动化测试 一:软件需求变动不频繁 二:项目周期较长 自动化测试框架的设计.脚本的开发与调试需要 ...
- 转载-对js中new、prototype的理解
说明:本篇文章是搜集了数家之言,综合的结果,应向数家致谢 说到prototype,就不得不先说下new的过程. 我们先看看这样一段代码: <script type="text/java ...
- python+selenium—webdriver入门(二)
本文中主要介绍webdriver常见的对象定位方法: 一.对象定位的目的 二.常见的对象定位方法 一.对象定位的目的: 1.操作对象 2.获得对象的属性,如:对象的class属性.name属性等 3. ...
- CentOS7 启动中文输入法
CentOS 系统中是带有中文输入法的(智能拼音),启动方式如下: Applications --> System Tools --> Setting --> Region &am ...
- git(转载谢谢)
Git可视化极简易教程 —— Git GUI使用方法 (新版的git默认已不能自行选择中文语言了) 前言 之前一直想一篇这样的东西,因为最初接触时,我也认真看了廖雪峰的教程,但是似乎我觉得讲得有点 ...
- collection管理程序中不同类别的资源
在一个计算图中,可以通过collection管理不同类别的资源,如通过tf.add_to_collection函数可以将资源加入一个或多个集合中,然后通过tf.get_collection获取一个集合 ...
- Beta冲刺 (5/7)
Part.1 开篇 队名:彳艮彳亍团队 组长博客:戳我进入 作业博客:班级博客本次作业的链接 Part.2 成员汇报 组员1(组长)柯奇豪 过去两天完成了哪些任务 共享编辑文章的后端数据处理 部分代码 ...
- Linux下gcc编译控制动态库导出函数小结
根据说明文档“How To Write Shared Libraries"介绍, 有四种方法: 1. 在方法声明定义时,加修饰:__attribute__((visibility(" ...
- Visual Studio中xml文件使用app.config、web.config等的智能提示的方法
在.Net开发的过程中,有时我们需要使用Xml文件作为配置文件(基于某些情况的考虑),而不是app.config.web.config这种,但是我们在xml中配置时希望可以增加类似编辑app.conf ...