当我对修改其输入的函数进行基准测试时,我必须复制基准测试的每个循环的测试数据,并在我这样做时暂停计时器。这可能意味着如果我运行banana
,测试可能需要2分钟而不是1秒。
我做错了什么还是我必须忍受这个?
更多背景信息:
我正在编写一个用于读取系统日志日志的程序。我的日志记录范例的一部分是记录消息的第一行包含可读文本,后面的行包含"额外信息",就像堆栈跟踪一样。因此,我的日志阅读器(除其他外)在第一个换行符上拆分消息,该换行符由rsyslog转义为go test -bench MyTest -benchtime 1s
。
以下是代码:
#012
它最初使用// Splits the main line from extra information
func splitMessageExtra(line *string) string {
var prev rune
for i, char := range *line {
if prev == 0 && char == '#' {
prev = char
continue
}
if prev == '#' && char == '0' {
prev = char
continue
}
if prev == '0' && char == '1' {
prev = char
continue
}
if prev == '1' && char == '2' {
extra := (*line)[i+1:]
*line = (*line)[0 : i-3]
return extra
}
prev = 0
}
return ""
}
并返回了新的字符串,但是cpu profiling显示它太慢了。
以下是基准功能:
strings.Split
这是一个没有暂停计时器的运行:
$ go test -bench SplitMessageExtra -benchtime 1s BenchmarkSplitMessageExtra-8 3000000 434 ns/op PASS ok github.com/Hubro/logreader 1.730s
这是一个具有上述确切基准函数的运行:
$ go test -bench SplitMessageExtra -benchtime 1s BenchmarkSplitMessageExtra-8 5000000 385 ns/op PASS ok github.com/Hubro/logreader 100.563s
请注意,需要运行AGES。
答案 0 :(得分:1)
您的代码和基准测试似乎很慢。这是一个更快的版本。
package main
import (
"strings"
"testing"
)
// Splits the main line from extra information
func splitMessageExtra(line *string) string {
const newline = "#012"
i := strings.Index(*line, newline)
if i < 0 {
return ""
}
extra := (*line)[i+len(newline):]
*line = (*line)[0:i]
return extra
}
var testMessage = `Feb 10 15:16:20 foo_stats[-] (warning): [foo_stats.postfix, line 166, thread "processor_mta03"]: Skipped line because there is no context:#012Feb 10 15:16:20 mta03 postfix/qmgr[7419]: ABCDEF123: from=<>, size=24431, nrcpt=1 (queue active)`
func BenchmarkSplitMessageExtra(b *testing.B) {
for i := 0; i < b.N; i++ {
msg := testMessage
splitMessageExtra(&msg)
}
}
输出:
$ go test -bench=.
goos: linux
goarch: amd64
pkg: extra
BenchmarkSplitMessageExtra-4 50000000 32.2 ns/op
PASS
ok extra 1.647s
为了进行比较,以下是您的代码和基准测试的结果。您的代码和基准测试比我的慢:968 ns / op和50.184s,而32.2 ns / op和1.647s。
package main
import (
"testing"
)
// Splits the main line from extra information
func splitMessageExtra(line *string) string {
var prev rune
for i, char := range *line {
if prev == 0 && char == '#' {
prev = char
continue
}
if prev == '#' && char == '0' {
prev = char
continue
}
if prev == '0' && char == '1' {
prev = char
continue
}
if prev == '1' && char == '2' {
extra := (*line)[i+1:]
*line = (*line)[0 : i-3]
return extra
}
prev = 0
}
return ""
}
var testMessage = `Feb 10 15:16:20 foo_stats[-] (warning): [foo_stats.postfix, line 166, thread "processor_mta03"]: Skipped line because there is no context:#012Feb 10 15:16:20 mta03 postfix/qmgr[7419]: ABCDEF123: from=<>, size=24431, nrcpt=1 (queue active)`
func BenchmarkSplitMessageExtra(b *testing.B) {
for i := 0; i < b.N; i++ {
b.StopTimer()
msg := string([]byte(testMessage))
b.StartTimer()
splitMessageExtra(&msg)
}
}
输出:
$ go test -bench=.
goos: linux
goarch: amd64
pkg: extra
BenchmarkSplitMessageExtra-4 2000000 968 ns/op
PASS
ok extra 50.184s
您的一些代码是不必要的;它使用CPU时间并触发分配。例如,将utf-8字节转换为符文for i, char := range *line {}
,将string
转换为[]byte
转换为string
,string([]byte(testMessage))
。一些算法可以改进。例如,搜索换行符。
答案 1 :(得分:0)
没有错。 StopTimer
和StartTimer
应该比splitMessageExtra
贵得多。他们都叫runtime.ReadMemStats
。请参阅here。