如何优化在 runtime.osyield 和 runtime.usleep 中花费最多时间的 golang 程序
How to optimize golang program that spends most time in runtime.osyield and runtime.usleep
我一直致力于优化分析社交图数据的代码(在 https://blog.golang.org/profiling-go-programs 的大量帮助下)并且我已经成功地修改了很多缓慢的代码。
所有数据先从db加载到内存,从那里分析数据出现CPU bound(最大内存消耗<10MB,CPU1@100%)
但现在我程序的大部分时间似乎都在 runtime.osyield 和 runtime.usleep 中。有什么方法可以防止这种情况发生?
我设置了 GOMAXPROCS=1 并且代码不会产生任何 goroutines(golang 库可能调用的除外)。
这是我从 pprof 得到的 top10 输出
(pprof) top10
62550ms of 72360ms total (86.44%)
Dropped 208 nodes (cum <= 361.80ms)
Showing top 10 nodes out of 77 (cum >= 1040ms)
flat flat% sum% cum cum%
20760ms 28.69% 28.69% 20850ms 28.81% runtime.osyield
14070ms 19.44% 48.13% 14080ms 19.46% runtime.usleep
11740ms 16.22% 64.36% 23100ms 31.92% _/C_/code/sc_proto/cloudgraph.(*Graph).LeafProb
6170ms 8.53% 72.89% 6170ms 8.53% runtime.memmove
4740ms 6.55% 79.44% 10660ms 14.73% runtime.typedslicecopy
2040ms 2.82% 82.26% 2040ms 2.82% _/C_/code/sc_proto.mAvg
890ms 1.23% 83.49% 1590ms 2.20% runtime.scanobject
770ms 1.06% 84.55% 1420ms 1.96% runtime.mallocgc
760ms 1.05% 85.60% 760ms 1.05% runtime.heapBitsForObject
610ms 0.84% 86.44% 1040ms 1.44% _/C_/code/sc_proto/cloudgraph.(*Node).DeepestChildren
(pprof)
_ /C_/code/sc_proto/* 函数是我的代码。
以及来自网络的输出:
(更好,SVG 版本的图形在这里:https://goo.gl/Tyc6X4)
我自己找到了答案,所以我将其张贴在这里以供遇到类似问题的其他人使用。特别感谢@JimB 让我走上了正确的道路。
从图中可以看出,通往osyield和usleep的路径都是垃圾回收例程。这个程序使用了一个链表,它生成了很多指针,这给 gc 带来了很多工作,它在清理我的混乱时偶尔会阻止我的代码的执行。
最终这个问题的解决方案来自 https://software.intel.com/en-us/blogs/2014/05/10/debugging-performance-issues-in-go-programs(顺便说一句,这是一个很棒的资源)。我按照那里有关内存分析器的说明进行了操作;并且用切片替换指针集合的建议解决了我的垃圾收集问题,我的代码现在快多了!
我一直致力于优化分析社交图数据的代码(在 https://blog.golang.org/profiling-go-programs 的大量帮助下)并且我已经成功地修改了很多缓慢的代码。
所有数据先从db加载到内存,从那里分析数据出现CPU bound(最大内存消耗<10MB,CPU1@100%)
但现在我程序的大部分时间似乎都在 runtime.osyield 和 runtime.usleep 中。有什么方法可以防止这种情况发生?
我设置了 GOMAXPROCS=1 并且代码不会产生任何 goroutines(golang 库可能调用的除外)。
这是我从 pprof 得到的 top10 输出
(pprof) top10
62550ms of 72360ms total (86.44%)
Dropped 208 nodes (cum <= 361.80ms)
Showing top 10 nodes out of 77 (cum >= 1040ms)
flat flat% sum% cum cum%
20760ms 28.69% 28.69% 20850ms 28.81% runtime.osyield
14070ms 19.44% 48.13% 14080ms 19.46% runtime.usleep
11740ms 16.22% 64.36% 23100ms 31.92% _/C_/code/sc_proto/cloudgraph.(*Graph).LeafProb
6170ms 8.53% 72.89% 6170ms 8.53% runtime.memmove
4740ms 6.55% 79.44% 10660ms 14.73% runtime.typedslicecopy
2040ms 2.82% 82.26% 2040ms 2.82% _/C_/code/sc_proto.mAvg
890ms 1.23% 83.49% 1590ms 2.20% runtime.scanobject
770ms 1.06% 84.55% 1420ms 1.96% runtime.mallocgc
760ms 1.05% 85.60% 760ms 1.05% runtime.heapBitsForObject
610ms 0.84% 86.44% 1040ms 1.44% _/C_/code/sc_proto/cloudgraph.(*Node).DeepestChildren
(pprof)
_ /C_/code/sc_proto/* 函数是我的代码。
以及来自网络的输出:
(更好,SVG 版本的图形在这里:https://goo.gl/Tyc6X4)
我自己找到了答案,所以我将其张贴在这里以供遇到类似问题的其他人使用。特别感谢@JimB 让我走上了正确的道路。
从图中可以看出,通往osyield和usleep的路径都是垃圾回收例程。这个程序使用了一个链表,它生成了很多指针,这给 gc 带来了很多工作,它在清理我的混乱时偶尔会阻止我的代码的执行。
最终这个问题的解决方案来自 https://software.intel.com/en-us/blogs/2014/05/10/debugging-performance-issues-in-go-programs(顺便说一句,这是一个很棒的资源)。我按照那里有关内存分析器的说明进行了操作;并且用切片替换指针集合的建议解决了我的垃圾收集问题,我的代码现在快多了!