如果我需要在每次迭代中重置,如何进行基准测试?
How to benchmark if I need a reset in each iteration?
我使用回溯编写了一个小型数独求解器。现在我想对这个函数的速度进行基准测试。这是我当前的代码:
type Board struct {
Cells [9][9]int
}
func BenchmarkBacktrack(b *testing.B) {
for i := 0; i < b.N; i++ {
b.StopTimer()
// prevent the modification of the orignal board
copy := &Board{
Cells: exampleBoard.Cells,
}
b.StartTimer()
copy.Backtrack()
}
}
因为 &Board
是指针,所以我会在第一次迭代中解决数独问题,在下一次迭代中我会回溯已解决的板。因此,我在每次迭代开始时重置电路板。 exampleBoard
填充了示例值。
他们是否是一种更好的方法来对功能进行基准测试,而无需一遍又一遍地停止和重新启动计时器?
函数调用不会花费少量时间来影响基准测试吗?
您可以尝试提供一个 func NewBoard([9][9]int) *Board
方法,它只从示例数据初始化一个板。然后在新板上为 Backtrack()
写一个基准,为 NewBoard()
.
写一个单独的基准
将这两个数字相减应该可以让您大致了解回溯方法的速度。
type Board struct {
Cells [9][9]int
}
var scratch *Board
func NewBoard(cells [9][9]int) *Board {
return &Board{Cells: cells}
}
func BenchmarkBacktrack(b *testing.B) {
for i := 0; i < b.N; i++ {
scratch = NewBoard(exampleBoard.Cells)
scratch.Backtrack()
}
func BenchmarkNewBoard(b *testing.B) {
for i := 0; i < b.N; i++ {
scratch = NewBoard(exampleBoard.Cells)
}
还要注意 scratch
变量的使用。尝试在基准循环内创建循环局部变量可能会导致编译器根据 presence/absence 的副作用优化对 NewBoard()
的调用。对于奇偶校验,您需要在两个基准测试中使用 scratch
变量。
And wouldn't cost the function calls a small amount of time that that impacts the benchmark?
当然会。 for
循环也是如此,它包含在基准测试中。加上调用 copy.Backtrack
函数的开销。但问题是,这应该是无关紧要的,除非您对单个操作进行基准测试需要纳秒(在这种情况下您不应该这样做)。创建一个空板可能是一个微不足道的操作,所以我根本不会碰定时器。如果它不是微不足道的,那么你做对了——调用 StopTimer
。这正是发明它的原因:
StopTimer stops timing a test. This can be used to pause the timer while performing complex initialization that you don't want to measure.
我使用回溯编写了一个小型数独求解器。现在我想对这个函数的速度进行基准测试。这是我当前的代码:
type Board struct {
Cells [9][9]int
}
func BenchmarkBacktrack(b *testing.B) {
for i := 0; i < b.N; i++ {
b.StopTimer()
// prevent the modification of the orignal board
copy := &Board{
Cells: exampleBoard.Cells,
}
b.StartTimer()
copy.Backtrack()
}
}
因为 &Board
是指针,所以我会在第一次迭代中解决数独问题,在下一次迭代中我会回溯已解决的板。因此,我在每次迭代开始时重置电路板。 exampleBoard
填充了示例值。
他们是否是一种更好的方法来对功能进行基准测试,而无需一遍又一遍地停止和重新启动计时器?
函数调用不会花费少量时间来影响基准测试吗?
您可以尝试提供一个 func NewBoard([9][9]int) *Board
方法,它只从示例数据初始化一个板。然后在新板上为 Backtrack()
写一个基准,为 NewBoard()
.
将这两个数字相减应该可以让您大致了解回溯方法的速度。
type Board struct {
Cells [9][9]int
}
var scratch *Board
func NewBoard(cells [9][9]int) *Board {
return &Board{Cells: cells}
}
func BenchmarkBacktrack(b *testing.B) {
for i := 0; i < b.N; i++ {
scratch = NewBoard(exampleBoard.Cells)
scratch.Backtrack()
}
func BenchmarkNewBoard(b *testing.B) {
for i := 0; i < b.N; i++ {
scratch = NewBoard(exampleBoard.Cells)
}
还要注意 scratch
变量的使用。尝试在基准循环内创建循环局部变量可能会导致编译器根据 presence/absence 的副作用优化对 NewBoard()
的调用。对于奇偶校验,您需要在两个基准测试中使用 scratch
变量。
And wouldn't cost the function calls a small amount of time that that impacts the benchmark?
当然会。 for
循环也是如此,它包含在基准测试中。加上调用 copy.Backtrack
函数的开销。但问题是,这应该是无关紧要的,除非您对单个操作进行基准测试需要纳秒(在这种情况下您不应该这样做)。创建一个空板可能是一个微不足道的操作,所以我根本不会碰定时器。如果它不是微不足道的,那么你做对了——调用 StopTimer
。这正是发明它的原因:
StopTimer stops timing a test. This can be used to pause the timer while performing complex initialization that you don't want to measure.