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

写性能测试在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. Linux 系统 pptpd+radius+mysql 安装攻略

    分类: 原文地址:Linux 系统 pptpd+radius+mysql 安装攻略 作者:wfeng .你所需要的软件 内核最好能升级到2.6 如果你是centos的用户,可以通过yum update ...

  2. RSAProtectedConfigurationProvider加密web.config

    上一篇文章介绍了用 DataProtectionConfigurationProvider加密web.config文件的方法,不过他有一个缺陷,加密的文件只有在本机才能解密,如果有多台服务器的话,则需 ...

  3. 如何借助Monit搭建服务器监控系统?(1)

    许多Linux管理员依赖一种集中式远程监控系统(比如Nagios或Cacti),检查网络基础设施的健康状况.虽然集中式监控系统为管理员在处理许多主机和设备时简化了工作,但专用的监控设备显然成了单一故障 ...

  4. DiskLrucCache使用Demo(强烈推荐,非常好用)

    DiskLrucCache使用的Demo,这个demo是从网络获取一张图片,保存到本地缓存中(sdcard和内存),当下载成功后.再打开不会又一次向网络请求图片.而是世界使用本地资源. 要使用Disk ...

  5. 基于FPGA的DDR3多端口读写存储管理系统设计

    基于FPGA的DDR3多端口读写存储管理系统设计 文章出处:电子技术设计 发布时间: 2015/03/12 | 1747 次阅读 每天新产品 时刻新体验专业薄膜开关打样工厂,12小时加急出货   机载 ...

  6. uva 11400 - Lighting System Design(动态规划 最长上升子序列问题变型)

    本题难处好像是在于 能够把一些灯泡换成电压更高的灯泡以节省电源的钱 .所以也才有了对最优方案的探求 好的处理方法是依照电压从小到大排序.仅仅能让前面的换成后面的.也就满足了把一些灯泡换成电压更高的灯泡 ...

  7. numpy.ravel()/numpy.flatten()/numpy.squeeze()

    numpy.ravel(a, order='C') Return a flattened array numpy.chararray.flatten(order='C') Return a copy ...

  8. 前端_basic

    web: 分三部分:1.HTML:2.CSS:3.JavaScript. 1.HTML:用来构建网页的结构和内容: 2.CSS:用来给网页化妆,美化网页: 3.JavaScript:用来让网页呈现动态 ...

  9. mybatis传多个参数实例

    最近在做一个统计功能,有一个功能点:根据id更新某字段的值.那么就需要有两个参数,我的做法: dao层: int updateTaskCount(int taskCount,int id); 对应的m ...

  10. 可执行文件格式elf和bin

    区别 常用的可执行文件包含两类:原始二进制文件(bin)和可加载执行的二进制文件,在linux中可加载执行的二进制文件为elf文件. BIN文件是直接的二进制文件,内部没有地址标记.bin文件内部数据 ...