最近在线上发现一块代码逻辑在执行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. 解决find命令报错: paths must precede expression(转)

    原文地址:https://www.cnblogs.com/peter1994/p/7297656.html 在一天早上,想在服务器 /tmp 目录清除一些pdf文件,大概一万多个文件,在执行命令的时候 ...

  2. Tomcat9报错 The valid characters are defined in RFC 7230 and RFC 3986

    tomcat8项目移到tomcat9,出现如下问题 HTTP Status 400 – Bad Request Type Exception Report Message Invalid charac ...

  3. SAS对数据变量的处理

    SAS对数据变量的处理 在使用DATA步基于已经存在的数据集生成新数据集时,可以指定在新数据集中不需要包含的变量而仅读取其他变量,或者指定仅需要在 新数据集中包含的变量.该功能可以通过DATA步中的S ...

  4. python伪装浏览器

    def get_content(url): req_header = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/53 ...

  5. 【慕课网实战】Spark Streaming实时流处理项目实战笔记十三之铭文升级版

    铭文一级: 第10章 Spark Streaming整合Kafka spark-submit \--class com.imooc.spark.KafkaReceiverWordCount \--ma ...

  6. IntelliJ IDEA 2018.1.3 破解方法之一

    IntelliJ IDEA 2018.1.3 破解方法之一 声明:如果资金充足请购买正版! NO1 下载安装IntelliJ IDEA 企业版 NO2 下载jar包 (1)下载地址:http://id ...

  7. uniGUI经验几则

    uniGUI经验几则 (2015-11-07 21:42:41) 转载▼ 标签: it 分类: uniGUI 1.uniTimer的妙用 很多时候,都会遇到在一个uniForm或者uniFrame加载 ...

  8. SDWebImage之SDImageCache

    SDImageCache和SDWebImageDownloader是SDWebImage库的最重要的两个部件,它们一起为SDWebImageManager提供服务,来完成图片的加载.SDImageCa ...

  9. WITH RECOMPILE和OPTION(RECOMPILE)区别仅仅是存储过程级重编译和SQL语句级重编译吗

    在考虑重编译T-SQL(或者存储过程)的时候,有两种方式可以实现强制重编译(前提是忽略导致重编译的其他因素的情况下,比如重建索引,更新统计信息等等), 一是基于WITH RECOMPILE的存储过程级 ...

  10. Interview Common Sample Codes

    1. Quick Sort: int partition(int A[], int p, int r) { int x = A[r]; // Pivot element int i = p - 1; ...