如何对修改其输入的函数进行基准测试?

时间:2017-02-26 00:06:24

标签: go benchmarking

当我对修改其输入的函数进行基准测试时,我必须复制基准测试的每个循环的测试数据,并在我这样做时暂停计时器。这可能意味着如果我运行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。

2 个答案:

答案 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转换为stringstring([]byte(testMessage))。一些算法可以改进。例如,搜索换行符。

答案 1 :(得分:0)

没有错。 StopTimerStartTimer应该比splitMessageExtra贵得多。他们都叫runtime.ReadMemStats。请参阅here