对于一些服务来说,性能是极其重要的一环,事关系统的吞吐、访问的延迟,进而影响用户的体验。

写性能测试在Go语言中是很便捷的,go自带的标准工具链就有完善的支持,下面我们来从Go的内部和系统调用方面来详细剖析一下Benchmark这块儿。

Benchmark


Go做Benchmar只要在目录下创建一个_test.go后缀的文件,然后添加下面函数:

func BenchmarkStringJoin1(b *testing.B) {
b.ReportAllocs()
input := []string{"Hello", "World"}
for i := ; i < b.N; i++ {
result := strings.Join(input, " ")
if result != "Hello World" {
b.Error("Unexpected result: " + result)
}
}
}

调用以下命令:

# go test -run=xxx -bench=. -benchtime="3s" -cpuprofile profile_cpu.out

该命令会跳过单元测试,执行所有benchmark,同时生成一个cpu性能描述文件.

这里有两个注意点:

  • -benchtime 可以控制benchmark的运行时间
  • b.ReportAllocs() ,在report中包含内存分配信息,例如结果是:
BenchmarkStringJoin1-   ns/op  B/op  allocs/op

-4表示4个CPU线程执行;300000表示总共执行了30万次;4531ns/op,表示每次执行耗时4531纳秒;32B/op表示每次执行分配了32字节内存;2 allocs/op表示每次执行分配了2次对象。

根据上面的信息,我们就能对热点路径进行内存对象分配的优化,例如针对上面的程序我们可以进行小小的优化:

func BenchmarkStringJoin2(b *testing.B) {
b.ReportAllocs()
input := []string{"Hello", "World"}
join := func(strs []string, delim string) string {
if len(strs) == {
return strs[] + delim + strs[];
}
return "";
};
for i := ; i < b.N; i++ {
result := join(input, " ")
if result != "Hello World" {
b.Error("Unexpected result: " + result)
}
}
}

新的Benchmark结果是:

BenchmarkStringJoin2-   ns/op  B/op  allocs/op

可以看出来,在减少了内存分配后,性能提升了60%以上!

Cpu Profile


上一节的benchmark结果,我们只能看到函数的整体性能,但是如果该函数较为复杂呢?然后我们又想知道函数内部的耗时,这时就该Cpu Profile登场了。

Cpu profile是Go语言工具链中最闪耀的部分之一,掌握了它以及memory、block profile,那基本上就没有你发现不了的性能瓶颈了。

之前的benchmark同时还生成了一个profile_cpu.out文件,这里我们执行下面的命令:

# go tool pprof app.test profile_cpu.out
Entering interactive mode (type "help" for commands)
(pprof) top10
8220ms of 10360ms total (79.34%)
Dropped nodes (cum <= .80ms)
Showing top nodes out of (cum >= 160ms)
flat flat% sum% cum cum%
2410ms 23.26% 23.26% 4960ms 47.88% runtime.concatstrings
2180ms 21.04% 44.31% 2680ms 25.87% runtime.mallocgc
1200ms 11.58% 55.89% 1200ms 11.58% runtime.memmove
530ms 5.12% 61.00% 530ms 5.12% runtime.memeqbody
530ms 5.12% 66.12% 2540ms 24.52% runtime.rawstringtmp
470ms 4.54% 70.66% 2420ms 23.36% strings.Join
390ms 3.76% 74.42% 2330ms 22.49% app.BenchmarkStringJoin3B
180ms 1.74% 76.16% 1970ms 19.02% runtime.rawstring
170ms 1.64% 77.80% 5130ms 49.52% runtime.concatstring3
160ms 1.54% 79.34% 160ms 1.54% runtime.eqstring

上面仅仅展示部分函数的信息,并没有调用链路的性能分析,因此如果需要完整信息,我们要生成svg或者pdf图。

# go tool pprof -svg profile_cpu.out > profile_cpu.svg
# go tool pprof -pdf profile_cpu.out > profile_cpu.pdf

下面是profile_cpu.pdf的图:

 

可以看到图里包含了多个benchmark的合集(之前的两段benmark函数都在同一个文件中),但是我们只关心性能最差的那个benchmark,因此需要过滤:

go test -run=xxx -bench=BenchmarkStringJoin2B$ -cpuprofile profile_2b.out
go test -run=xxx -bench=BenchmarkStringJoin2$ -cpuprofile profile_2.out
go tool pprof -svg profile_2b.out > profile_2b.svg
go tool pprof -svg profile_2.out > profile_2.svg

 

根据图片展示,benchmark自身的函数(循环之外的函数)runtime.concatstrings触发了内存对象的分配,造成了耗时,但是跟踪到这里,我们已经无法继续下去了,因此下面就需要flame graphs 了。

“A flame graph is a good way to drill down your benchmarks, finding your bottlenecks #golang” via @TitPetric

 

如果想详细查看,你只要点击这些矩形块就好。

 

生成这些图,我们需要 uber/go-torch这个库,这个库使用了https://github.com/brendangregg/FlameGraph,下面是一个自动下载依赖,然后生成frame graph的脚本,读者可以根据需要,自己实现。

#!/bin/bash
# install flamegraph scripts
if [ ! -d "/opt/flamegraph" ]; then
echo "Installing flamegraph (git clone)"
git clone --depth= https://github.com/brendangregg/FlameGraph.git /opt/flamegraph
fi # install go-torch using docker
if [ ! -f "bin/go-torch" ]; then
echo "Installing go-torch via docker"
docker run --net=party --rm=true -it -v $(pwd)/bin:/go/bin golang go get github.com/uber/go-torch
# or if you have go installed locally: go get github.com/uber/go-torch
fi PATH="$PATH:/opt/flamegraph"
bin/go-torch -b profile_cpu.out -f profile_cpu.torch.svg

至此,我们的benchmark之路就告一段落,但是上面所述的cpu profile不仅仅能用在benchmark中,还能直接在线debug生产环境的应用性能,具体的就不详细展开,该系列后续文章会专门讲解。

完整源码

package main

import "testing"
import "strings" func BenchmarkStringJoin1(b *testing.B) {
b.ReportAllocs()
input := []string{"Hello", "World"}
for i := ; i < b.N; i++ {
result := strings.Join(input, " ")
if result != "Hello World" {
b.Error("Unexpected result: " + result)
}
}
} func BenchmarkStringJoin1B(b *testing.B) {
b.ReportAllocs()
for i := ; i < b.N; i++ {
input := []string{"Hello", "World"}
result := strings.Join(input, " ")
if result != "Hello World" {
b.Error("Unexpected result: " + result)
}
}
} func BenchmarkStringJoin2(b *testing.B) {
b.ReportAllocs()
input := []string{"Hello", "World"}
join := func(strs []string, delim string) string {
if len(strs) == {
return strs[] + delim + strs[];
}
return "";
};
for i := ; i < b.N; i++ {
result := join(input, " ")
if result != "Hello World" {
b.Error("Unexpected result: " + result)
}
}
} func BenchmarkStringJoin2B(b *testing.B) {
b.ReportAllocs()
join := func(strs []string, delim string) string {
if len(strs) == {
return strs[] + delim + strs[];
}
return "";
};
for i := ; i < b.N; i++ {
input := []string{"Hello", "World"}
result := join(input, " ")
if result != "Hello World" {
b.Error("Unexpected result: " + result)
}
}
} func BenchmarkStringJoin3(b *testing.B) {
b.ReportAllocs()
input := []string{"Hello", "World"}
for i := ; i < b.N; i++ {
result := input[] + " " + input[];
if result != "Hello World" {
b.Error("Unexpected result: " + result)
}
}
} func BenchmarkStringJoin3B(b *testing.B) {
b.ReportAllocs()
for i := ; i < b.N; i++ {
input := []string{"Hello", "World"}
result := input[] + " " + input[];
if result != "Hello World" {
b.Error("Unexpected result: " + result)
}
}
}
 

原文: http://www.jianshu.com/p/cb8a95cc66f0

Go语言性能测试的更多相关文章

  1. 易语言关于使用CURL,网页_访问,网页_访问S,网页_访问_对象,鱼刺(winHttpW)发送Get性能测试

    易语言关于使用 CURL,网页_访问,网页_访问S,网页_访问_对象,鱼刺(winHttpW)发送Get性能测试 测试模块情况: |-精易模块5.8  |-鱼刺类Http  |-libCURL +++ ...

  2. 从三个语言(C++,Java,.Net)的几个性能测试案例来看性能优化

    随着时间的发展,现在的虚拟机技术越来越成熟了,在有些情况下,Java,.Net等虚拟机密集计算的性能已经和C++相仿,在个别情况下,甚至还要更加优秀.本文详细分析几个性能测试案例,探讨现象背后的原因. ...

  3. C语言qsort函数算法性能测试

    对于该算法的复杂性.一个直接的方法是测量的一定量的算法级数据的执行时间的感知. 随着C语言提供qsort对于示例.随着100一万次的数据,以测试其计算量.感知O(nlg(n))时间成本: C码如下面: ...

  4. Golang 语言的单元测试和性能测试(也叫 压力测试)

    Golang单元测试对文件名和方法名,参数都有很严格的要求. 例如: 1.文件名必须以xx_test.go命名 2.方法必须是Test[^a-z]开头(T必须大写),func TestXxx (t * ...

  5. Web前端性能测试-性能测试知多少---深入分析前端站点的性能

    针对目前接手的web前端的性能,一时间不知道从什么地方入手,然后经过查找资料,发现其实还是蛮简单的. 前端性能测试对象: HTML.CSS.JS.AJAX等前端技术开发的Web页面 影响用户浏览网页速 ...

  6. Probe在性能测试中的使用方式简介

    简介: Lambda Probe(以前称为Tomcat Probe)是一款实时监控和管理的Apache Tomcat实例的基本工具. Lambda Probe 是基于 Web + AJAX 的强大的免 ...

  7. Jmeter性能测试 入门

    Jmeter是一款优秀的开源测试工具, 是每个资深测试工程师,必须掌握的测试工具,熟练使用Jmeter能大大提高工作效率. 熟练使用Jmeter后, 能用Jmeter搞定的事情,你就不会使用LoadR ...

  8. Elong App 性能测试分享

    个人简介: 测试老鸟,曾做过6年的测试以及2年的大数据开发:曾就职于伟景行.高德(大数据开发):钟情于钻研开源测试框架:目前挂单于艺龙. 有对本主题感兴趣的同学,可以加我Q私信(305285925): ...

  9. Android性能测试工具APT使用指南

    腾讯的安卓平台高效的性能测试工具APT(Android Performance Testing Tools),适用于开发自测和定位性能瓶颈,帮助测试人员完成性能基准测试.竞品测试. APT提供了CPU ...

随机推荐

  1. top命令中内存参数

    总结:VIRT 虚拟内存中含有共享库.共享内存.栈.堆,所有已申请的总内存空间.RES  是进程正在使用的内存空间(栈.堆),申请内存后该内存段已被重新赋值.SHR  是共享内存正在使用的空间.SWA ...

  2. asp.net mvc5 配置自定义路径

    首先配置路由文件,默认页是第一个路由的配置: using System; using System.Collections.Generic; using System.Linq; using Syst ...

  3. (2.1)Smali系列学习之基础语法

    一.什么是Smali?Smali,Baksmali分别是指安卓系统里的Java虚拟机(Dalvik)所使用的一种dex格式文件的汇编器,反汇编器.其语法是一种宽松式的Jasmin/dedexer语法, ...

  4. iOS开发之使用XMPPFramework实现即时通信

    iOS开发之使用XMPPFramework实现即时通信   关于XMPP的理论介绍在本篇博客中就不做赘述了,如何在我们之前的微信中加入XMPP协议来实现通信呢?下面将会介绍一下XMPP的基本的知识,让 ...

  5. 【Android】利用Fiddler进行抓包详解教程。抓取接口以及数据,可以抓真实安卓手机或者模拟器。

    大家都知道抓包的方法很多.我这里给大家介绍介绍一种,利用fiddler进行抓包,当然比如Wireshark也可以抓包,我们这里不做介绍.我这里演示的是fiddler+天天模拟器,当然真实安卓手机也是一 ...

  6. IBATIS中‘$’与‘#’使用

    IBATIS中关于iterate和‘$’与‘#’的应用 一个包含List元素的HashMap参数赋给sqlMap  public int getCountById(String id, String ...

  7. linux学习笔记30--网络命令ifconfig

    许多windows非常熟悉ipconfig命令行工具,它被用来获取网络接口配置信息并对此进行修改.Linux系统拥有一个类似的工具,也就是ifconfig(interfaces config).通常需 ...

  8. 跟着百度学PHP[15]-会话控制session的工作机制

    COOKIE和SESSION的两大区别: cookie是存储与客户端 session是存储与服务端 需要开启session的时候需要使用session_start开启,且session的开头不能拥有任 ...

  9. 初识layer遮罩层

    背景:楼主做了一个先删除数据再插入的功能,但是狂点菜单的时候会有重复数据插入进来,设置字段unique之后,再狂点,控制台也会报错. 为了防止这种问题出现,我采取了制止”狂点“这种行为出现的做法,所以 ...

  10. MapReduce实战(一)自定义类型

    需求: 处理以下流量数据,第1列是手机号,第7列是上行流量,第8列是下行流量.将手机号一样的用户进行合并,上行流量汇总,下行流量也汇总,并相加求得总流量. 1363157985066 13726230 ...