英文:
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()
// 防止修改原始棋盘
copy := &Board{
Cells: exampleBoard.Cells,
}
b.StartTimer()
copy.Backtrack()
}
}
由于&Board
是指针,我会在第一次迭代中解决数独,在下一次迭代中会回溯一个已解决的棋盘。因此,我在每次迭代开始时重置棋盘。exampleBoard
中填充了示例值。
有没有更好的方法在不停止和重新启动计时器的情况下对函数进行基准测试?
而且函数调用会不会花费一小部分时间影响基准测试?
英文:
I've written a small Sudoku solver using backtracking. Now I want to benchmark the speed of this function. Here is my current code:
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()
}
}
Since &Board
is pointer I would solve the Sudoku in the first iteration and in the next one I would backtrack a solved board. Therefore, I reset the board at the beginning of each iteration. exampleBoard
is filled with sample values.
Is their a better way to benchmark the function without stopping and restarting the timer over and over?
And wouldn't cost the function calls a small amount of time that impacts the benchmark?
答案1
得分: 1
你可以尝试提供一个func NewBoard([9][9]int) *Board
方法,它只是从示例数据初始化一个棋盘。然后为Backtrack()
编写一个针对新棋盘的基准测试,并为NewBoard()
编写一个单独的基准测试。
通过两个数字的差异,你可以了解到Backtrack
方法的速度。
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
变量。尝试在基准测试循环内创建一个循环局部变量可能会导致编译器根据副作用的存在与否优化掉对NewBoard()
的调用。为了保持一致性,你需要在两个基准测试中都使用scratch
变量。
英文:
You could try providing a func NewBoard([9][9]int) *Board
method, which just initializes a board from the example data. Then write a benchmark for Backtrack()
on a new board and a separate benchmark for NewBoard()
.
Subtracting the two numbers should give you an idea of the speed of your Backtrack method alone.
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)
}
Also note the use of scratch
variable. Trying to create a loop local variable inside the benchmark loop could lead the compiler to optimise away the call to NewBoard()
depending on presence/absence of side-effects. For parity, you need to use the scratch
variable in both benchmarks.
答案2
得分: 1
当然会有影响。for
循环也会影响基准测试。此外,调用copy.Backtrack
函数也会有开销。但问题是,这些都是无关紧要的,除非你要对一个耗时几纳秒的单个操作进行基准测试(在这种情况下,你不应该这样做)。创建一个空白的棋盘可能是一个微不足道的操作,所以我不会改动计时器。如果不是微不足道的操作,那么你做得对——调用StopTimer
。这就是它被发明的原因:
StopTimer
停止计时测试。这可以用于在执行不想测量的复杂初始化时暂停计时器。
英文:
> And wouldn't cost the function calls a small amount of time that that impacts the benchmark?
Of course they would. So does the for
loop, which is included in the benchmark. Plus overhead of calling copy.Backtrack
function. But the thing is, this should be all irrelevant, unless you're benchmarking a single operation taking nanoseconds (in which case you shouldn't). Creation of an empty board is probably a trivial operation, so I wouldn't touch the timers at all. If it's not trivial, then you're doing it right – call StopTimer
. This is exactly why it was invented:
> StopTimer stops timing a test. This can be used to pause the timer while performing complex initialization that you don't want to measure.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论