-1

当我为我的算法编写基准测试时,我被一个问题弄糊涂了!

我的测试代码详细信息已推送到 github,我将其复制到此处并添加了一些注释。

https://github.com/hidstarshine/Algorithm/blob/master/leet/problem24_test.go

var TDBenchmarkSwapPairs1 *leet.ListNode

// This function may be not good, it should be init()?
func FTDBenchmarkSwapPairs1() {
    TDBenchmarkSwapPairs1 = &leet.ListNode{
        Val:  0,
        Next: nil,
    }
    changeNode := TDBenchmarkSwapPairs1
    for i := 1; i < 100; i++ {
        changeNode.Next = &leet.ListNode{
            Val:  i,
            Next: nil,
        }
        changeNode = changeNode.Next
    }
}

func BenchmarkSwapPairs1(b *testing.B) {
    FTDBenchmarkSwapPairs1() // problem is here
    for i := 0; i < b.N; i++ {
        leet.SwapPairs1(TDBenchmarkSwapPairs1)
    }
}

在问题行中,我调用 FTDBenchmarkSwapPairs1(FTD 表示填充测试数据)来初始化数据。

然后发生了一些令人惊奇的事情,BenchmarkSwapPairs1 似乎在许多 goroutine 中运行。

因此并发带来了数据竞争,并且由于 SwapPairs1 特殊的逻辑,调试变得一团糟。

SwapPairs1 将更改 ListNode 中的 Next。

然后我想将 BenchmarkSwapPairs1 移动到 for 块来解决这个问题。

但是数据竞赛似乎仍未解决,并且由于初始化时间的原因,基准测试没有任何意义。

我在 leetcode 上判断算法并被接受!

问:我怎样才能优雅地解决这个问题?需要一个好主意!


新的@Jimb

我添加只是添加一些调试信息然后它恐慌。我也认为它一开始不会有数据竞赛。

当我看到恐慌时,我做出了假设!

package leet_test

import (
    "fmt"
    "testing"

    "github.com/hidstarshine/Algorithm/leet"
)

var TDBenchmarkSwapPairs1 *leet.ListNode

func FTDBenchmarkSwapPairs1() {
    TDBenchmarkSwapPairs1 = &leet.ListNode{
        Val:  0,
        Next: nil,
    }
    changeNode := TDBenchmarkSwapPairs1
    for i := 1; i < 100; i++ {
        changeNode.Next = &leet.ListNode{
            Val:  i,
            Next: nil,
        }
        changeNode = changeNode.Next
    }
    AnotherChangeNode := TDBenchmarkSwapPairs1
    for AnotherChangeNode != nil {
        fmt.Println(AnotherChangeNode)
        AnotherChangeNode = AnotherChangeNode.Next
    }
}

func BenchmarkSwapPairs1(b *testing.B) {
    FTDBenchmarkSwapPairs1()
    for i := 0; i < b.N; i++ {
        fmt.Println(TDBenchmarkSwapPairs1.Next)
        fmt.Println(TDBenchmarkSwapPairs1.Next.Next)
        fmt.Println(TDBenchmarkSwapPairs1.Next.Next.Next)
        fmt.Println(TDBenchmarkSwapPairs1.Next.Next.Next.Next)
        leet.SwapPairs1(TDBenchmarkSwapPairs1)
    }
}


恐慌信息(重要)

more...

&{98 0xc000044ac0}
&{99 <nil>}
&{1 0xc000044270}
&{2 0xc0000444a0}
&{3 0xc0000444c0}
&{4 0xc0000444d0}

Some system info

&{15 0xc000044ae0}
&{2 0xc000044bd0}
&{17 0xc000044b00}
&{4 0xc000044bf0}
&{17 0xc000044ae0}

Unorderd message

<nil>
&{4 0xc000044ae0}
&{2 <nil>}
<nil>
panic: runtime error: invalid memory address or nil pointer dereference // why
[signal 0xc0000005 code=0x0 addr=0x8 pc=0xbefb20]
4

1 回答 1

4

如果您有多个基准函数,您可能不希望它们干扰彼此的数据,因此请使用局部变量而不是(共享)包级变量。

您可以使用*B.ResetTimer从整体基准运行时间中删除设置时间。

func BenchmarkSwapPairs1(b *testing.B) {
    root := &leet.ListNode{
        Val:  0,
        Next: nil,
    }
    changeNode := root
    for i := 1; i < 10000; i++ {
        changeNode.Next = &leet.ListNode{
            Val:  i,
            Next: nil,
        }
        changeNode = changeNode.Next
    }
    b.ResetTimer()

    for i := 0; i < b.N; i++ {
        root = leet.SwapPairs1(root)
    }
}
于 2021-07-15T15:39:33.843 回答