开发Go应用程序或对其进行基准测试以找到改进方法的常见操作是对函数执行时间进行基准测试。我介绍了Go中的CPU和内存配置文件已经存在,但这是不同的,更适合临时功能执行时间的测量。
确定两个表达式之间的时间间隔:使用time.Since
time.Since
是由time
标准库程序包需要一个Time
值,计算与当前时间的差,然后返回Duration
值,它是一个int64类型,带有String()
附带的方法,它为计算的时间提供了人性化的符号。
import (
"fmt"
"time"
)
func main() {
start := time.Now()
<span style="color:#75715e">//... do something
fmt.Println(time.Since(start))
}
在内部,time.Since
是的简写time.Now().Sub(t)
。
使用defer
测量功能内部的时间
您可以通过使用defer
:
package main
import (
“log”
“time”
)
func runningtime(s string) (string, time.Time) {
log.Println("Start: ", s)
return s, time.Now()
}
func track(s string, startTime time.Time) {
endTime := time.Now()
log.Println("End: ", s, “took”, endTime.Sub(startTime))
}
func execute() {
defer track(runningtime(“execute”))
time.Sleep(3 * time.Second)
}
func main() {
execute()
}
通过多次运行来对功能性能进行基准测试:使用testing
包裹
这是我写的一个简单的库,它计算字符串中的符文数。
runecount.go
package runecount
import “unicode/utf8”
func RuneCount(s string) int {
return len([]rune(s))
}
func RuneCount2(s string) int {
return utf8.RuneCountInString(s)
}
计算字符串中的符文的两种方法中哪一种更快?我们通过编写基准来找出答案。基准存在于*_test.go
文件,例如常规测试,并且可以保留以以下各项开头的测试方法Test*
,但它们以Benchmark*
反而:
runecount_test.go
package runecount
import “testing”
func BenchmarkRuneCount(b testing.B) {
s := “Gophers are amazing 😁”
for i := 0; i < b.N; i++ {
RuneCount(s)
}
}
func BenchmarkRuneCount2(b testing.B) {
s := “Gophers are amazing 😁”
for i := 0; i < b.N; i++ {
RuneCount2(s)
}
}
我通过在文件所在的同一文件夹中执行来运行此命令:
go test -bench=.
这将导致所有基准测试运行。我只有一个文件,但是如果有很多文件,则可以指定要运行的文件。
每个基准运行循环,并传递bN变量,该变量由基准运行程序自动确定,直到平均值稳定到足以确定结果为止。
这是我在Mac上获得的输出:
# flavio @ Flavios-MacBook-Pro in ~/go/src/github.com/flaviocopes/snippets/benchmark on git:master x [18:32:26]
$ go test -bench=.
BenchmarkRuneCount-2 20000000 115 ns/op
BenchmarkRuneCount2-2 30000000 44.1 ns/op
PASS
ok github.com/flaviocopes/snippets/benchmark 3.812s
20000000
和30000000
是运行的操作数。
该命令返回基准测试结果:运行了2000万次RuneCount()
,平均耗时163纳秒,然后运行了3000万次RuneCount2()
,每次迭代平均需要74.2纳秒。
所以Benchmark1
,它运行RuneCount()
,比慢2倍RuneCount2()
,即使用内置utf8.RuneCountInString
功能。毫不奇怪,因为utf8.RuneCountInString
针对此特定任务进行了高度优化。
而不是使用go test
,您也可以致电testing.Benchmark
从命令:
package main
import (
“fmt”
“testing”
“runecount”
)
func BenchmarkRuneCount(b testing.B) {
s := “Gophers are amazing 😁”
for i := 0; i < b.N; i++ {
RuneCount(s)
}
}
func BenchmarkRuneCount2(b testing.B) {
s := “Gophers are amazing 😁”
for i := 0; i < b.N; i++ {
RuneCount2(s)
}
}
func main() {
fmt.Println(testing.Benchmark(BenchmarkRuneCount))
fmt.Println(testing.Benchmark(BenchmarkRuneCount2))
}
在哪里阅读更多
- https://dave.cheney.net/2013/06/30/how-to-write-benchmarks-in-go
- http://www.soroushjp.com/2015/01/27/beautifully-simple-benchmarking-with-go/
更多教程:
- 使用NGINX反向代理服务Go服务
- 在Go中复制结构
- Go Web服务器的基础
- 在Go中对地图类型进行排序
- 简而言之去指针
- 转到标签说明
- 开始日期和时间格式
- 使用Go进行JSON处理
- 可变参数函数
- 去弦备忘单
- 转到空界面说明
- 使用VS Code和Delve调试Go
- 命名为Go返回参数
- 在Go中生成随机数和字符串
- Go项目的文件系统结构
- Go中的二进制搜索算法
- 在Go中使用命令行标志
- GOPATH解释
- 使用Go构建一个命令行应用程序:lolcat
- 使用Go构建CLI命令:Cowsay
- 在Go中使用壳管
- Go CLI教程:财富克隆
- 使用Go列出文件夹中的文件
- 使用Go从GitHub获取存储库列表
- 去,将一小段字符串附加到文件中
- 去,将字符串转换为字节片
- 使用Go可视化您本地的Git贡献
- Go CPU和内存分析入门
- 解决Go程序中的“不支持索引”错误
- 测量Go程序中的执行时间
- 使用Go构建Web爬网程序以检测重复的标题
- 最佳实践:指针还是价值接收者?
- 最佳实践:您应该使用方法还是函数?
- Go数据结构:集
- 前往地图备忘单
- 在Go中生成泛型类型的实现
- Go数据结构:字典
- Go数据结构:哈希表
- 在“通过通道”中实现事件侦听器
- Go数据结构:堆栈
- Go数据结构:队列
- Go数据结构:二进制搜索树
- Go数据结构:图形
- Go数据结构:链表
- Go数据结构的完整指南
- 比较Go值
- Go是面向对象的吗?
- 在Go中使用SQL数据库
- 在Go中使用环境变量
- 上篇教程:PostgreSQL支持的REST API
- 在Go Web服务器上启用CORS
- 在Docker容器中部署Go应用程序
- 为什么Go是作为PHP开发人员学习的功能强大的语言
- 去,删除io.Reader.ReadString换行符
- 开始,如何观看更改并重建程序
- 去算一下自约会以来的月份
- 在Go中访问HTTP POST参数