最近在线上发现一块代码逻辑在执行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逃逸分析和竞争检测的更多相关文章

  1. Golang逃逸分析

    Golang逃逸分析 介绍逃逸分析的概念,go怎么开启逃逸分析的log. 以下资料来自互联网,有错误之处,请一定告之. sheepbao 2017.06.10 什么是逃逸分析 wiki上的定义 In ...

  2. 聊聊Golang逃逸分析

    逃逸分析的概念,go怎么开启逃逸分析的log. 以下资料来自互联网,有错误之处,请一定告之. 什么是逃逸分析 wiki上的定义 在编译程序优化理论中,逃逸分析是一种确定指针动态范围的方法——分析在程序 ...

  3. 逃逸分析与栈、堆分配分析 escape_analysis

    小结: 1.当形参为 interface 类型时,在编译阶段编译器无法确定其具体的类型.因此会产生逃逸,最终分配到堆上. 2.The construction of a value doesn't d ...

  4. 基于Golang的逃逸分析(Language Mechanics On Escape Analysis)

    何为逃逸分析 在编译程序优化理论中,逃逸分析是一种确定指针动态范围的方法——分析在程序的哪些地方可以访问到指针.它涉及到指针分析和形状分析. 当一个变量(或对象)在子程序中被分配时,一个指向变量的指针 ...

  5. JVM笔记-逃逸分析

    参考: http://www.iteye.com/topic/473355http://blog.sina.com.cn/s/blog_4b6047bc01000avq.html 什么是逃逸分析(Es ...

  6. Go变量逃逸分析

    目录 什么是逃逸分析 为什么要逃逸分析 逃逸分析是怎么完成的 逃逸分析实例 总结 写过C/C++的同学都知道,调用著名的malloc和new函数可以在堆上分配一块内存,这块内存的使用和销毁的责任都在程 ...

  7. 逃逸分析(Escape Analysis)

    一.什么是逃逸 逃逸是指在某个方法之内创建的对象,除了在方法体之内被引用之外,还在方法体之外被其它变量引用到:这样带来的后果是在该方法执行完毕之后,该方法中创建的对象将无法被GC回收,由于其被其它变量 ...

  8. 深入理解Java中的逃逸分析

    在Java的编译体系中,一个Java的源代码文件变成计算机可执行的机器指令的过程中,需要经过两段编译,第一段是把.java文件转换成.class文件.第二段编译是把.class转换成机器指令的过程. ...

  9. java虚拟机的逃逸分析

    逃逸分析作为其他优化手段提供依据的分析技术,其基本行为就是分析对象动态作用域:当一个对象在方法中被定义后,它可能被外部方法所引用,例如作为调用参数传递到其他方法中,称为方法逃逸.甚至还有可能被外部线程 ...

随机推荐

  1. 自定义View(四) ViewGroup 动态添加变长Tag标签 支持自动换行

    欲实现如下效果: 思路很简单就2步: 1.测量出ViewGroup的大小 2.找出子View的位置 若要实现动态添加标签view,就要实现ViewGroup的onMeasure().onLayout( ...

  2. idea打包含第三方依赖的jar包

    1.打开idea,打开java项目,选择file-->Project Structure,添加依赖的jar包 2.配置artfacts 点击ok,不需要做任何操作 点击jar,右键新建一个lib ...

  3. 粒子动画——Pygame

    你是否也想做出下图这么漂亮的动态效果?想的话就跟着我一起做吧=.= 工具: Python--Pygame 仔细观察上图,你能发现哪些机制呢?再在下面对比一下是否跟你想的一样. 运行机制: 1.随机方向 ...

  4. linux学习第九天 (Linux就该这么学)

    今天讲了raid0 至少两块盘串联在一起,读写性能提升,但不具备数据备份和错误修复能力,RAID1把两块盘绑定,在写入数据时,同时写入到多块硬盘设备,raid5推荐使用,10推荐使用  LVM,今天是 ...

  5. 通过flask的request对象获取url

    测试了一下:通过发送 GET 到 http://127.0.0.1:5000/test/a?x=1, 后台输出为(官网说明): 1 request.path: /test/a 2 request.ho ...

  6. 别人的Linux私房菜(7)文件与目录管理

    - 代表上一个工作目录 ~username代表用户所在的家目录 cd切换目录  配合之上的参数 .   ..   /    ~    ~name     (change directory) pwd显 ...

  7. ios 导航push跳转方向设置

    CATransition* transition = [CATransition animation]; transition.type = kCATransitionPush;//可更改为其他方式 ...

  8. ASM的一些小坑

    变量必需放到数据段,才有直接对地址赋值的访问权限 segment .data n1 dw 55h segment .text global _nasm_function _nasm_function: ...

  9. 第41章:MongoDB-集群--Sharding(分片)

    ①Sharding分片概念 分片(sharding)是指将数据库拆分,将其分散在不同的机器上的过程.将数据分散到不同的机器上,不需要功能强大的服务器就可以存储更多的数据和处理更大的负载. 分片是每个分 ...

  10. OpenCV2.4.10 + VS2010开发环境配置

    原文转载自:qinyang8513 一.开发环境 1.操作系统:Windows 7(64位) 2.编程环境:Microsoft Visual Studio 2010 3.OpenCV版本:2.4.10 ...