设为首页收藏本站

LUPA开源社区

 找回密码
 注册
文章 帖子 博客
LUPA开源社区 首页 业界资讯 技术文摘 查看内容

Go程序的性能调试问题

2015-3-30 20:49| 发布者: joejoe0332| 查看: 3195| 评论: 0|原作者: 社会主义好, Micooz, 暗夜在火星, ScriptKid, 霍啸林|来自: oschina

摘要: 假设你手上有个Go语言编写的程序,你打算提升它的性能。目前有一些工具可以为此提供帮助。这些工具能帮你发现包括CPU、IO和内存在内多种类型的热点。所谓热点,是指那些为了能显著提升性能而值得你去关注的地方。有 ...
  假设你手上有个Go语言编写的程序,你打算提升它的性能。目前有一些工具可以为此提供帮助。这些工具能帮你发现包括CPU、IO和内存在内多种类型的热点。所谓热点,是指那些为了能显著提升性能而值得你去关注的地方。有时候这些工具还能帮助你发现程序中主要的性能瑕疵。举个例子,你没必要每次执行SQL查询前都对SQL语句进行参数化解析,你可以将这个准备过程在程序启动时一次完成。再举个例子,当前某个算法的复杂度是O(N²),但其实存在一个复杂度是O(N)的解决方案。为了能发现这些问题,需要理智地检查你在优化分析器中获取到的信息。比如上面提到的第一个问题,你会注意到相当长的时间被花费在了对SQL语句的准备上。

  了解针对性能的不同边界因素也是比较重要的。比方说,如果一个程序使用100 Mbps带宽的网络进行通信,而目前已经占用了超过90 Mbps的带宽,为了提升它的性能,你拿这样的程序也没啥办法了。在磁盘IO、内存消耗和计算密集型任务方面,也有类似的边界因素。


  将这点牢记在心,让我们看看有哪些工具可以用。


  注意:这些工具会彼此互相影响。例如,对内存使用优化分析器会导致针对CPU的优化分析器产生误差,对goroutine阻塞使用优化分析器会影响调度器跟踪等等。为了获得更加精确的信息,请在隔离的环境中使用这些工具。


  注意:本文描述的用法基于Go语言发布的1.3版。


CPU 分析器

  Go 运行时包含了内建的CPU分析器,它用来展示某个函数耗费了多少CPU百分时间。这里有三种方式来使用它:


1. 最简单的是用"go test"的-cpuprofile选项。例如下面的命令:

$ go test -run=none -bench=ClientServerParallel4 -cpuprofile=cprof net/http

将会分析所给的基准并将结果写入"cprof"文件中。

然后:

$ go tool pprof --text http.test cprof

将会打印耗费最多CPU时间的函数列表。

这里有几种可用的输出形式,最实用的有 --text, --web 和 --list。运行 "go tool pprof" 来得到完整的列表。

这个选项最明显的缺点是它只能用来做测试。

2. net/http/pprof 包。这是网络服务器的理想解决方案。你可能仅仅需要导入net/http/pprof,然后使用下面的方法收集分析结果:

$ go tool pprof --text mybin http://myserver:6060:/debug/pprof/profile

3. 手动收集. 你需要导入 runtime/pprof 然后再main函数中添加下面的代码:

1
2
3
4
5
6
7
8
   if *flagCpuprofile != "" {
       f, err := os.Create(*flagCpuprofile)
       if err != nil {
           log.Fatal(err)
       }
       pprof.StartCPUProfile(f)
       defer pprof.StopCPUProfile()
   }

分析结果会被写入指定的文件中,像第一种方式一样使之可视化。

这里有一个使用 --web 选项来实现可视化的例子:

  你可以使用--list=funcname来审查单一函数。例如,下面的结果显示了附加函数中的时间流逝:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    .      .   93: func (bp *buffer) WriteRune(r rune) error {
    .      .   94:     if r < utf8.RuneSelf {
    5      5   95:         *bp = append(*bp, byte(r))
    .      .   96:         return nil
    .      .   97:     }
    .      .   98: 
    .      .   99:     b := *bp
    .      .  100:     n := len(b)
    .      .  101:     for n+utf8.UTFMax > cap(b) {
    .      .  102:         b = append(b, 0)
    .      .  103:     }
    .      .  104:     w := utf8.EncodeRune(b[n:n+utf8.UTFMax], r)
    .      .  105:     *bp = b[:n+w]
    .      .  106:     return nil
    .      .  107: }

  你可以在这里找到pprof工具的详细信息以及上图中数字的描述。


  在3种特殊的情形下分析器不能解开堆栈:GC,System和ExternalCode。GC 表示垃圾回收期间的用时,查看下面的内存分析器和垃圾回收跟踪器以得到优化建议。System 表示goroutine调度程序,栈管理代码和其他辅助运行时代码的用时。ExternalCode 表示本地动态库耗时。


  对于你在简介中看到的东西的解释,这里有一些提示和技巧。


  如果你看到大量的时间消耗在运行时间,内存分配的函数,那么暗示程序产生了大量过度的小内存分配工作。此描述将会告诉你这些分配来自哪里。查看内存分析器部分可以获得如何优化这种情况的建议。可考虑对程序进行重新调整以消除频繁对共享资源的获取和接入。对此,一般的解决技术方案包括有分片/分区,本地缓存/计量和写时拷贝。


  如果大量的时间消耗在频道操作,同步。互斥代码和其他同步原语或者系统容器,那么程序很可能正在饱受资源争夺的痛苦。

如果大量的时间消耗在系统调用的读/写,那么暗示程序产生了大量过度的小块读写。对这种情况,围绕系统文件或者网络连接而包装的Bufio会很有帮助。


  如果大量的时间消耗在GC容器,那么程序要么分配了大量的短暂临时的对象,要么堆栈的空间非常小以致垃圾回收收集变得非常频繁。通过查看垃圾收集追踪器和内存分析器这两部分可以得到一些优化的建议。


  温馨提示:当前CPU分析器不能工作于darwin


  温馨提示:在windows系统上你需要安装Cygwin, Perl和Graphviz才能生成svg/web简介。


  温馨提示:在linux系统上你也可以尝试PERF系统分析器。它不能解开Go的栈,但它可以获得cgo或者SWIG的代码和kernel内核的快照并解开。所以它对于洞悉本地/kernel内核的性能瓶颈非常有帮助。



酷毙

雷人

鲜花

鸡蛋

漂亮
  • 快毕业了,没工作经验,
    找份工作好难啊?
    赶紧去人才芯片公司磨练吧!!

最新评论

关于LUPA|人才芯片工程|人才招聘|LUPA认证|LUPA教育|LUPA开源社区 ( 浙B2-20090187 浙公网安备 33010602006705号   

返回顶部