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虚拟机的逃逸分析
逃逸分析作为其他优化手段提供依据的分析技术,其基本行为就是分析对象动态作用域:当一个对象在方法中被定义后,它可能被外部方法所引用,例如作为调用参数传递到其他方法中,称为方法逃逸.甚至还有可能被外部线程 ...
随机推荐
- MYSQL(Mariadb)
CentOS7下安装MariaDB 添加 MariaDB yum 仓库(官网的,也可以直接用aliyun云的) vi /etc/yum.repos.d/MariaDB.repo [mariadb] n ...
- Linux查看某个进程的线程
线程是现代操作系统上进行并行执行的一个流行的编程方面的抽象概念.当一个程序内有多个线程被叉分出用以执行多个流时,这些线程就会在它们之间共享特定的资源(如,内存地址空间.打开的文件),以使叉分开销最小化 ...
- CentOS7中firewall防火墙详解和配置,.xml服务配置详解
修改防火墙配置文件之前,需要对之前防火墙做好备份 重启防火墙后,需要确认防火墙状态和防火墙规则是否加载,若重启失败或规则加载失败,则所有请求都会被防火墙 1. firewall-cmd --state ...
- javaweb开发2.新建一个javaweb项目
1.File → New → Dynamic Web Project 2.创建一个Dynamic Web Project 3.点击“Next”下一步 4.点击“Next”下一步 5.点击“Finish ...
- gitlab 10汉化
记得备份 先检查一下版本,好下载对应的汉化包 cat /opt/gitlab/embedded/service/gitlab-rails/VERSION 1)然后下载10.0.x.diff 文件到服务 ...
- python之高阶函数
1.函数的参数能接收变量 def calc(x): return x*x n = 10 print(calc(n)) #输出为100 2.变量指向函数 def calc(x): return x*x ...
- xbee无线通讯终端
无线数传电台支持DIGI全系列无线数传模块,包括有xbee/xbee PRO S1.xbee/xbee PRO S2C.xbee WIFI.xbee PRO S3B.xbee/xbee PRO SX( ...
- instr()函数--支持模糊查询
1)instr()函数的格式 (俗称:字符查找函数) 格式一:instr( string1, string2 ) / instr(源字符串, 目标字符串) 格式二:instr( strin ...
- 51nod1683
代码参考:http://blog.csdn.net/sdfzyhx/article/details/74359927 //dp[i][j][k]表示到了第i列,最下边的点最短路为j,剩下的点之间的关系 ...
- document.getElementById("xx").style.xxx中的 全部属性
CSS语法(不区分大小写) JavaScript语法(区分大小写) border border border-bottom borderBottom border-bottom-color borde ...