diff --git a/pcvaluetab/bench_test.go b/pcvaluetab/bench_test.go index d2c904b..bdd8648 100644 --- a/pcvaluetab/bench_test.go +++ b/pcvaluetab/bench_test.go @@ -6,10 +6,13 @@ package main import ( "flag" + "fmt" + "math" "math/rand" "os" "path/filepath" "strings" + "sync" "testing" ) @@ -39,13 +42,27 @@ func BenchmarkDecode(b *testing.B) { } for _, bin := range flagBinary.list { - b.Run(filepath.Base(bin), func(b *testing.B) { - decode1(b, bin) + b.Run("bin="+filepath.Base(bin), func(b *testing.B) { + symtab := LoadSymTab(bin) + + for _, pcMode := range []uint32{pcModeRandom, 0, 4096} { + label := fmt.Sprint(pcMode) + if pcMode == pcModeRandom { + label = "random" + } + + b.Run("pc="+label, func(b *testing.B) { + decode1(b, symtab, pcMode) + }) + } + if *flagSizeStats { + fileSizeStats(b, bin, symtab) + } }) } } -func decode1(b *testing.B, bin string) { +func fileSizeStats(b *testing.B, bin string, symtab *SymTab) { var fileBytes int if stat, err := os.Stat(bin); err != nil { b.Fatal(err) @@ -53,10 +70,8 @@ func decode1(b *testing.B, bin string) { fileBytes = int(stat.Size()) } - symtab := LoadSymTab(bin) - var varintBytes, altBytes int - if *flagSizeStats { + gather := sync.OnceFunc(func() { // Collect the total size of the varint and alt tables. altDups := map[string]bool{} for _, tab := range symtab.PCTabs { @@ -70,8 +85,34 @@ func decode1(b *testing.B, bin string) { altDups[string(altTab)] = true altBytes += len(altTab) } - } + }) + + // This is a bit goofy. We're not measuring time, so the normal testing.B + // looping doesn't work. We start a sub-benchmark so that -test.bench + // selection works, and then print out own result and call SkipNow to + // prevent looping. This hack means results won't align nicely, and if this + // this is the first "benchmark" to run, our results will appear before the + // benchmark tags. + + b.Run("enc=varint", func(b *testing.B) { + gather() + fmt.Printf("%s\t%d\t%v table-bytes\t%v file-bytes\n", b.Name(), 1, varintBytes, fileBytes) + b.SkipNow() + }) + b.Run("enc=alt", func(b *testing.B) { + gather() + altFileBytes := fileBytes - varintBytes + altBytes + fmt.Printf("%s\t%d\t%v table-bytes\t%v file-bytes\n", b.Name(), 1, altBytes, altFileBytes) + // Metrics get kind of weird with "percent change", so just log it. + fmt.Printf("file-bytes alt versus varint: %+f%%\n", diffPct(fileBytes, altFileBytes)) + b.SkipNow() + }) +} + +const pcModeRandom = math.MaxUint32 + +func decode1(b *testing.B, symtab *SymTab, pcMode uint32) { // Random sample of tables. const nSamples = 1024 type sample struct { @@ -80,32 +121,34 @@ func decode1(b *testing.B, bin string) { textLen uint32 pc uint32 } - samples := make([]sample, nSamples) - for i := range samples { + samples := make([]sample, 0, nSamples) + for len(samples) < nSamples { // Pick a random table. var tab *VarintPCData for _, tab = range symtab.PCTabs { break } + // Pick a PC. + pc := pcMode + if pcMode == math.MaxUint32 { + pc = uint32(rand.Intn(int(tab.TextLen))) + } else if pc >= tab.TextLen { + // Try again + continue + } // Re-encode it. altTab := linearIndex(tab) - // Pick a random PC. - pc := uint32(rand.Intn(int(tab.TextLen))) - samples[i] = sample{tab, altTab, tab.TextLen, pc} + samples = append(samples, sample{tab, altTab, tab.TextLen, pc}) } - b.Run("varint", func(b *testing.B) { + b.Run("enc=varint", func(b *testing.B) { for i := 0; i < b.N; i++ { sample := &samples[i%len(samples)] lookupVarintPCData(sample.varintTab.Raw, uintptr(sample.pc), nil) } - if *flagSizeStats { - b.ReportMetric(float64(varintBytes), "table-bytes") - b.ReportMetric(float64(fileBytes), "file-bytes") - } }) - b.Run("varint-cache-nohit", func(b *testing.B) { + b.Run("enc=varint/cachehit=0", func(b *testing.B) { var cache pcvalueCache for i := 0; i < b.N; i++ { // In practice this will never hit in the cache because there @@ -114,7 +157,7 @@ func decode1(b *testing.B, bin string) { lookupVarintPCData(sample.varintTab.Raw, uintptr(sample.pc), &cache) } }) - b.Run("varint-cache-hit", func(b *testing.B) { + b.Run("enc=varint/cachehit=7:1", func(b *testing.B) { var cache pcvalueCache for i := 0; i < b.N; i++ { // Hit 7 times out of 8. That's probably dramatically higher @@ -123,21 +166,10 @@ func decode1(b *testing.B, bin string) { lookupVarintPCData(sample.varintTab.Raw, uintptr(sample.pc), &cache) } }) - first := true - b.Run("alt", func(b *testing.B) { + b.Run("enc=alt", func(b *testing.B) { for i := 0; i < b.N; i++ { sample := &samples[i%len(samples)] lookupLinearIndex(sample.altTab, sample.textLen, sample.pc) } - if *flagSizeStats { - b.ReportMetric(float64(altBytes), "table-bytes") - altFileBytes := fileBytes - varintBytes + altBytes - b.ReportMetric(float64(altFileBytes), "file-bytes") - if first { - // Metrics get kind of weird with "percent change", so just log it. - b.Logf("file-bytes change versus varint: %+f%%", diffPct(fileBytes, altFileBytes)) - first = false - } - } }) }