专栏首页程序员LIYI有人说 Go 语言性能不行,大佬不服,使用 pprof 三步优化代码,性能堪比 C++

有人说 Go 语言性能不行,大佬不服,使用 pprof 三步优化代码,性能堪比 C++

这篇文章部分转自:https://www.cnblogs.com/qcrao-2018/p/11832732.html

目录

1,CPU 优化

2,内存优化

3,GC 优化

4,小结


这篇文章讲了一个故事,大意是 Go 语言大佬 Russ Cox 发现一篇文章说使用 Go 写的算法很慢,而 C++ 很快。大佬哪受得了这个,于是使用 Go 语言性能优化神器 pprof,开始了优化实战。最终经过三轮优化,基本上能达到和 C++ 同等的速度和同样的内存消耗。这三轮优化十分具有借签与学习之意义,简述如下:

1,CPU 优化

首先,使用如下代码开启 CPU 监测:

pprof.StartCPUProfile(f)

耗时 top5 的函数,发现一个读 map 的函数耗时最长:mapaccess1_fast64,而它出现在一个递归函数中。执行 web mapaccess1 命令,聚焦出调用 mapaccess1_fast64 函数最多的就是 main.FindLoops 和 main.DFS,再执行命令:list DFS,定位到相关的具体代码。

优化的方法是将 map 改成 slice。当然并不是所有的map都需要这样做,在这个案例中能这样做,和 key 的类型是 int 而且不是太稀疏有关。具体问题需要具体分析,这正是 pprof 工具的价值所在。

修改完之后,再次通过 cpu profiling,发现递归函数 mapaccess1_fast64 的耗时已经不在 top5 中了,但是新增了长耗时函数:runtime.mallocgc,占比 54.2%,而这和内存分配以及垃圾回收相关。

2,内存优化

使用如下代码开启内存监测:

FindHavlakLoops(cfgraph, lsgraph)
f,_ := os.Create(*memprofile)
pprof.WriteHeapProfile(f)

继续通过 top5、list 命令找到内存分配最多的代码位置,发现这回是向 map 里插入元素使用的内存比较多。

改进方式同样是用 slice 代替 map。但 map 有一个缺点,就是可以重复插入元素,因此写一个向 slice 插入元素的函数,可以防止重复插入:

func appendUnique(a []int, x int) []int {
    for _, y := range a {
        if x == y {
            return a
        }
    }
    return append(a, x)
}

好了,现在程序比最初的时候快了 2.1 倍。再次查看 cpu profile 数据,发现 runtime.mallocgc 降了一些,但仍然占比 50.9%。因此需要查看垃圾回收到底在回收哪些内容,这些内容就是导致频繁垃圾回收的“罪魁祸首”。

3,GC 优化

使用 web mallocgc 命令,将和 mallocgc 相关的函数用矢量图的方式展现出来,但是有太多样本量很少的节点影响观察,增加过滤命令:

go tool pprof --nodefraction=0.1 profile

在这条指令中,将少于 10% 的采样点过滤掉。

(注:这是 pprof 生成的分析结果)

新的矢量图可以直观地看出,FindLoops 触发了最多的垃圾回收操作。继续使用命令 list FindLoops 直接找到代码的位置。原来,每次执行 FindLoops 函数时,都要 make 一些临时变量,这会加重垃圾回收器的负担。

改进方式是增加一个全局变量 cache,这样可以重复利用。虽然这样做的坏处是,线程不是安全的了。

4,小结

使用 pprof 工具进行的优化到这就结束了。最后值得提一下,大佬做这件事的时间比较久远,最初写于 2011 年,原文在这里:

https://blog.golang.org/profiling-go-programs

(Russ Cox 优化过程,并附上代码)

原文有更多内容和参考资料,如果有时间推荐查看。

pprof 的包路径是 "runtime/pprof",可以通用报告生成、Web 可视化界面、交互式终端 三种方式来使用。具体使用方法可查相关文档。

本文分享自微信公众号 - 程序员LIYI(CoderLIYI),作者:Stefno

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-11-13

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 【1/25】将Game改写为单例模式(Singleton Pattern)

    Game的实例在游戏时只有一个,现在我们首先将Game类改造为一个单例。所谓单例,就是运行时只有一个实例。Game.js代码改造如下:

    程序员LIYI
  • 22 内联样式的绑定

    内联样式的绑定,与class绑定一样,vue同样做了基于对象绑定与数组绑定的优化。

    程序员LIYI
  • 互斥锁与读写锁:如何使用锁完成Go程同步?

    这张图容易让人产生误解,容易让人误以为goroutine1获取的锁,只有goroutine1能释放,其实不是这样的。“秦失其鹿,天下共逐之”。在这张图中,gor...

    程序员LIYI
  • Linux文件查找命令详解

    对于文件查找,我们最好用的还是属于find命令了,在说find命令之前,先把另外几个查找命令介绍一下。

    信安本原
  • Golang包——pprof

    pprof 是用于可视化和分析性能分析数据的工具 CPU Profiling:CPU 分析,按照一定的频率采集所监听的应用程序 CPU(含寄存器)的使用情况,...

    羊羽shine
  • 1.2万亿晶体管,史上最强AI芯片诞生:英伟达Titan V的57倍

    这款名为 Wafer Scale Engine(WSE)的芯片拥有 1.2 万亿个晶体管,其数量是英伟达最新一代旗舰 GPU Titan V 的 57 倍。它的...

    机器之心
  • 5-4 使用 webpack-dev-server 实现请求转发

    请求转发,其实是使用 webpack-dev-server 的代理功能来实现的,本节为大家介绍 webpack-dev-server 的代理功能和主要使用场景。

    love丁酥酥
  • DeepLearning.ai学习笔记(三)结构化机器学习项目--week1 机器学习策略

    一、为什么是ML策略 ? 如上图示,假如我们在构建一个喵咪分类器,数据集就是上面几个图,训练之后准确率达到90%。虽然看起来挺高的,但是这显然并不具一般性,...

    marsggbo
  • shell介绍,命令历史,命令补全和别名,通配符,输入输出重定向

    Shell俗称壳(用来区别于核),是指“提供使用者使用界面”的软件(命令解析器)。shell类似于DOS下的command和后来的cmd.exe。它接收用户命令...

    端碗吹水
  • webpack基本打包配置流程

    项目搭建以及webpack打包配置流程 创建文件夹EC(文件夹和文件名根据需求自定义) 在EC文件夹下新建文件夹APP 在APP文件夹下新建入口文件index....

    用户1148399

扫码关注云+社区

领取腾讯云代金券