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

写性能测试在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. sklearn中xgboost模块中plot_importance函数(特征重要性)

    # -*- coding: utf-8 -*- """ ######################################################### ...

  2. laydate 和 Vue 奇怪的清空问题

    laydate的input,会自动被清空,当别的input修改的时候.改成这样既可解决 <td><input type="text" id="retur ...

  3. 跨Server查询

    GO RECONFIGURE GO GO RECONFIGURE GO SELECT MAX(OldestCall) FROM ( SELECT FeildAA As FeildAAFROM OPEN ...

  4. PHP+shell实现多线程的方法

    PHP+shell实现多线程的方法 这里介绍怎样借助shell脚本实现多线程. 先写个简单的php代码.这里为了让脚本运行时间更长.方便看效果,sleep一下.呵呵.先看下test.php的代码:ls ...

  5. atitit.eclipse有多少api  扩展点,以及扩展点的设计

    atitit.eclipse有多少api  扩展点,以及扩展点的设计 不赞成使用的.作废的以及内部的扩展点 [扩展]页显示了几个你不应该在你的插件中使用的扩展点.在附表C.1的[描述]栏中,我们使用如 ...

  6. [svc][op]如何查看当前Ubuntu系统的版本

    如何查看当前Ubuntu系统的版本 说来也惭愧,用Ubuntu差不多快1个月了,双系统是让朋友安的,只知道自己使用的是什么12版本的,具体怎么看还不知道,下面写一下查看当前Linux系统的版本的方法 ...

  7. erlang supervisor中启动普通的进程

    文字部分转自: http://1234n.com/?post/qou3eb supervisor的子进程 一开始使用supervisor的时候,我用的是init/1返回子进程规格列表的方式,并且所有子 ...

  8. Python unittest 参数化

    准备工作: pip install  nose_parameterized 典型场景:用户名.密码参数化 实例 1,新建一个ftl.py 文件 ,用来将存在于.txt .xlsx 文件中的参数化数据转 ...

  9. 一次csrf配合xss的攻击实例

    xss出现在我的邮箱处 alert of payload "><script>alert(/test/)</script><" by cooki ...

  10. IDEA2017 导入 SVN上的 Myeclipse或Eclipse 项目

    从SVN上检出项目,不用打开. 关闭IDEA后选择 import project,导入后按F4 (改成eclipse按键模式后 按ctrl alt shift F4)进入 project struct ...