cmd/benchstat: add -csv option for CSV output
The CSV output can more easily be imported into programs
like Sheets, Excel, LibreOffice, SPSS, etc. without any
ad hoc processing of the formatted output.
Includes tests.
Change-Id: I29e2fe6e4707932f1dee0a035d7b2d4a699567f8
Reviewed-on: https://go-review.googlesource.com/c/perf/+/158017
Run-TryBot: David Chase <drchase@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
diff --git a/benchstat/text.go b/benchstat/text.go
index 99ec736..55b7a26 100644
--- a/benchstat/text.go
+++ b/benchstat/text.go
@@ -5,8 +5,11 @@
package benchstat
import (
+ "encoding/csv"
"fmt"
"io"
+ "path/filepath"
+ "strings"
"unicode/utf8"
)
@@ -77,6 +80,90 @@
}
}
+func must(e error) {
+ if e != nil {
+ panic(e)
+ }
+}
+
+// FormatCSV appends a CSV formatting of the tables to w.
+// norange suppresses the range columns.
+func FormatCSV(w io.Writer, tables []*Table, norange bool) {
+ var textTables [][]*textRow
+ for _, t := range tables {
+ textTables = append(textTables, toCSV(t, norange))
+ }
+
+ for i, table := range textTables {
+ if i > 0 {
+ fmt.Fprintf(w, "\n")
+ }
+ csvw := csv.NewWriter(w)
+
+ // headings
+ row := table[0]
+ must(csvw.Write(row.cols))
+
+ // data
+ for _, row := range table[1:] {
+ must(csvw.Write(row.cols))
+ }
+ csvw.Flush()
+ }
+}
+
+// trimCommonPathPrefix returns a string slice with common
+// path-separator-terminated prefixes removed. Empty strings
+// are ignored and a singleton non-empty string is left unchanged
+func trimCommonPathPrefix(ss []string) []string {
+ commonPrefixLen := -1
+ commonPrefix := ""
+ trimCommonPrefix := false // true if more than one data row w/ non-empty title
+
+ // begin finding common byte prefix (could end on partial rune)
+ for _, s := range ss {
+ if s == "" {
+ continue
+ }
+ if commonPrefixLen == -1 {
+ commonPrefix = s
+ commonPrefixLen = len(s)
+ continue
+ }
+ trimCommonPrefix = true // More than one not-empty
+ if commonPrefixLen > len(s) {
+ commonPrefixLen = len(s)
+ commonPrefix = commonPrefix[0:commonPrefixLen]
+ }
+ for j := 0; j < commonPrefixLen; j++ {
+ if commonPrefix[j] != s[j] {
+ commonPrefixLen = j
+ commonPrefix = commonPrefix[0:commonPrefixLen]
+ break
+ }
+ }
+ }
+ if !trimCommonPrefix || commonPrefixLen == 0 {
+ return ss
+ }
+ // trim to include last path separator.
+ commonPrefixLen = 1 + strings.LastIndex(commonPrefix, string(filepath.Separator))
+ if commonPrefixLen == 0 {
+ // No separator found means commonPrefixLen = 1 + (-1) == 0
+ return ss
+ }
+
+ rs := []string{}
+ for _, s := range ss {
+ if s == "" {
+ rs = append(rs, "")
+ } else {
+ rs = append(rs, s[commonPrefixLen:])
+ }
+ }
+ return rs
+}
+
// A textRow is a row of printed text columns.
type textRow struct {
cols []string
@@ -86,6 +173,20 @@
return &textRow{cols: cols}
}
+// newTextRowDelta returns a labeled row of text, with "±" inserted after
+// each member of "cols" unless norange is true.
+func newTextRowDelta(norange bool, label string, cols ...string) *textRow {
+ newcols := []string{}
+ newcols = append(newcols, label)
+ for _, s := range cols {
+ newcols = append(newcols, s)
+ if !norange {
+ newcols = append(newcols, "±")
+ }
+ }
+ return &textRow{cols: newcols}
+}
+
func (r *textRow) add(col string) {
r.cols = append(r.cols, col)
}
@@ -107,7 +208,7 @@
textRows = append(textRows, newTextRow("name", "old "+t.Metric, "new "+t.Metric, "delta"))
default:
row := newTextRow("name \\ " + t.Metric)
- row.cols = append(row.cols, t.Configs...)
+ row.cols = append(row.cols, t.Configs...) // TODO Should this also trim common path prefix? (see toCSV)
textRows = append(textRows, row)
}
@@ -137,3 +238,56 @@
}
return textRows
}
+
+// toCSV converts the Table to a slice of rows containing CSV-separated data
+// If norange is true, suppress the range information for each data item.
+// If norange is false, insert a "±" in the appropriate columns of the header row.
+func toCSV(t *Table, norange bool) []*textRow {
+ var textRows []*textRow
+ units := ""
+ if len(t.Rows) > 0 && len(t.Rows[0].Metrics) > 0 {
+ units = " (" + t.Rows[0].Metrics[0].Unit + ")"
+ }
+ switch len(t.Configs) {
+ case 1:
+ textRows = append(textRows, newTextRowDelta(norange, "name", t.Metric+units))
+ case 2:
+ textRows = append(textRows, newTextRowDelta(norange, "name", "old "+t.Metric+units, "new "+t.Metric+units, "delta"))
+ default:
+ rowname := "name \\ " + t.Metric
+ row := newTextRowDelta(norange, rowname+units, trimCommonPathPrefix(t.Configs)...)
+ textRows = append(textRows, row)
+ }
+
+ var group string
+
+ for _, row := range t.Rows {
+ if row.Group != group {
+ group = row.Group
+ textRows = append(textRows, newTextRow(group))
+ }
+ text := newTextRow(row.Benchmark)
+ for _, m := range row.Metrics {
+ mean := fmt.Sprintf("%.5E", m.Mean)
+ diff := m.FormatDiff()
+ if m.Unit == "" {
+ mean = ""
+ diff = ""
+ }
+ text.cols = append(text.cols, mean)
+ if !norange {
+ text.cols = append(text.cols, diff)
+ }
+ }
+ if len(t.Configs) == 2 {
+ delta := row.Delta
+ text.cols = append(text.cols, delta)
+ text.cols = append(text.cols, row.Note)
+ }
+ textRows = append(textRows, text)
+ }
+ for _, r := range textRows {
+ r.trim()
+ }
+ return textRows
+}
diff --git a/cmd/benchstat/main.go b/cmd/benchstat/main.go
index 4b27d7f..b882c06 100644
--- a/cmd/benchstat/main.go
+++ b/cmd/benchstat/main.go
@@ -119,6 +119,8 @@
flagAlpha = flag.Float64("alpha", 0.05, "consider change significant if p < `α`")
flagGeomean = flag.Bool("geomean", false, "print the geometric mean of each file")
flagHTML = flag.Bool("html", false, "print results as an HTML table")
+ flagCSV = flag.Bool("csv", false, "print results in CSV form")
+ flagNoRange = flag.Bool("norange", false, "suppress range columns (CSV only)")
flagSplit = flag.String("split", "pkg,goos,goarch", "split benchmarks by `labels`")
flagSort = flag.String("sort", "none", "sort by `order`: [-]delta, [-]name, none")
)
@@ -187,6 +189,8 @@
buf.WriteString(htmlHeader)
benchstat.FormatHTML(&buf, tables)
buf.WriteString(htmlFooter)
+ } else if *flagCSV {
+ benchstat.FormatCSV(&buf, tables, *flagNoRange)
} else {
benchstat.FormatText(&buf, tables)
}
diff --git a/cmd/benchstat/main_test.go b/cmd/benchstat/main_test.go
index 29419ee..ed8aba9 100644
--- a/cmd/benchstat/main_test.go
+++ b/cmd/benchstat/main_test.go
@@ -10,9 +10,14 @@
"io/ioutil"
"os"
"os/exec"
+ "path/filepath"
"testing"
)
+func dotSlash(s string) string {
+ return "." + string(filepath.Separator) + s
+}
+
func TestGolden(t *testing.T) {
if err := os.Chdir("testdata"); err != nil {
t.Fatal(err)
@@ -43,6 +48,15 @@
check(t, "namesort", "-sort=name", "old.txt", "new.txt")
check(t, "deltasort", "-sort=delta", "old.txt", "new.txt")
check(t, "rdeltasort", "-sort=-delta", "old.txt", "new.txt")
+
+ check(t, "oldcsv", "-geomean", "-csv", "old.txt")
+ check(t, "allcsv", "-geomean", "-csv", "old.txt", "new.txt", "slashslash4.txt", "x386.txt")
+ check(t, "allnosplitcsv", "-geomean", "-csv", "-split", "", dotSlash("new.txt"), dotSlash("old.txt"), dotSlash("slashslash4.txt"), dotSlash("x386.txt")) // note order: new old slashslash4 x386; dotSlash tests common prefix removal
+
+ if dotSlash("x") == "./x" { // Golden files have hardcoded "/" path separators in them
+ check(t, "allnorangecsv", "-geomean", "-csv", "-norange", dotSlash("old.txt"), dotSlash("new.txt"), "slashslash4.txt", "x386.txt") // Mixed ./ tests common prefix non-removal
+ }
+
}
func check(t *testing.T, name string, files ...string) {
@@ -68,6 +82,7 @@
exit = func(code int) { t.Fatalf("exit %d during main", code) }
*flagGeomean = false
*flagHTML = false
+ *flagNoRange = false
*flagDeltaTest = "utest"
*flagSplit = flag.Lookup("split").DefValue
@@ -95,7 +110,8 @@
if len(data) > 0 {
return string(data)
}
- return "ERROR: " + err.Error()
+ // Most likely, "diff not found" so print the bad output so there is something.
+ return "ERROR: " + err.Error() + ": test output = \n" + string(old)
}
func writeTemp(t *testing.T, data []byte) string {
diff --git a/cmd/benchstat/testdata/allcsv.golden b/cmd/benchstat/testdata/allcsv.golden
new file mode 100644
index 0000000..7480ef2
--- /dev/null
+++ b/cmd/benchstat/testdata/allcsv.golden
@@ -0,0 +1,155 @@
+name \ time/op (ns/op),old.txt,±,new.txt,±,slashslash4.txt,±,x386.txt,±
+pkg:hash/crc32 goos:darwin goarch:amd64
+CRC32/poly=IEEE/size=15/align=0-8,4.68700E+01,8%,4.45200E+01,3%
+CRC32/poly=IEEE/size=15/align=1-8,4.47100E+01,5%,4.45000E+01,4%
+CRC32/poly=IEEE/size=40/align=0-8,4.10375E+01,1%,4.25000E+01,6%,4.20800E+01,3%
+CRC32/poly=IEEE/size=40/align=1-8,4.10778E+01,1%,4.20400E+01,3%,4.16800E+01,5%
+CRC32/poly=IEEE/size=512/align=0-8,2.38000E+02,5%,5.71200E+01,3%
+CRC32/poly=IEEE/size=512/align=1-8,2.35500E+02,3%,5.71700E+01,3%
+CRC32/poly=IEEE/size=1kB/align=0-8,4.52500E+02,4%,9.41125E+01,2%
+CRC32/poly=IEEE/size=1kB/align=1-8,4.43600E+02,2%,9.32875E+01,2%
+CRC32/poly=IEEE/size=4kB/align=0-8,1.74000E+03,8%,2.98111E+02,1%,1.68200E+03,2%
+CRC32/poly=IEEE/size=4kB/align=1-8,1.76430E+03,6%,2.99100E+02,3%,1.68980E+03,4%
+CRC32/poly=IEEE/size=32kB/align=0-8,1.49529E+04,7%,2.15800E+03,3%
+CRC32/poly=IEEE/size=32kB/align=1-8,1.41888E+04,7%,2.17830E+03,3%
+CRC32/poly=Castagnoli/size=15/align=0-8,1.63778E+01,3%,1.63000E+01,2%
+CRC32/poly=Castagnoli/size=15/align=1-8,1.72222E+01,2%,1.72900E+01,2%
+CRC32/poly=Castagnoli/size=40/align=0-8,1.74300E+01,2%,1.75300E+01,4%,1.85800E+01,11%
+CRC32/poly=Castagnoli/size=40/align=1-8,1.97100E+01,3%,1.93900E+01,2%,1.96375E+01,2%
+CRC32/poly=Castagnoli/size=512/align=0-8,4.01700E+01,2%,4.01300E+01,4%
+CRC32/poly=Castagnoli/size=512/align=1-8,4.21400E+01,3%,4.19444E+01,2%
+CRC32/poly=Castagnoli/size=1kB/align=0-8,6.55000E+01,1%,6.61625E+01,1%
+CRC32/poly=Castagnoli/size=1kB/align=1-8,7.00900E+01,6%,6.84667E+01,2%
+CRC32/poly=Castagnoli/size=4kB/align=0-8,1.62800E+02,5%,1.58800E+02,3%,1.61200E+02,8%
+CRC32/poly=Castagnoli/size=4kB/align=1-8,1.69400E+02,6%,1.61600E+02,3%,1.69600E+02,8%
+CRC32/poly=Castagnoli/size=32kB/align=0-8,1.21822E+03,4%,1.21433E+03,3%
+CRC32/poly=Castagnoli/size=32kB/align=1-8,1.26478E+03,3%,1.22080E+03,4%
+CRC32/poly=Koopman/size=15/align=0-8,3.65100E+01,11%,3.56000E+01,3%
+CRC32/poly=Koopman/size=15/align=1-8,3.51500E+01,5%,3.55111E+01,1%
+CRC32/poly=Koopman/size=40/align=0-8,9.16400E+01,9%,8.76500E+01,2%,9.38100E+01,13%
+CRC32/poly=Koopman/size=40/align=1-8,9.10800E+01,6%,8.80300E+01,3%,8.68600E+01,3%
+CRC32/poly=Koopman/size=512/align=0-8,1.13170E+03,5%,1.07590E+03,3%
+CRC32/poly=Koopman/size=512/align=1-8,1.12680E+03,6%,1.16660E+03,8%
+CRC32/poly=Koopman/size=1kB/align=0-8,2.24333E+03,6%,2.34070E+03,4%
+CRC32/poly=Koopman/size=1kB/align=1-8,2.14867E+03,2%,2.36010E+03,5%
+CRC32/poly=Koopman/size=4kB/align=0-8,9.03150E+03,6%,9.00320E+03,6%,9.08160E+03,8%
+CRC32/poly=Koopman/size=4kB/align=1-8,8.94020E+03,10%,9.04630E+03,12%,9.45520E+03,8%
+CRC32/poly=Koopman/size=32kB/align=0-8,7.24280E+04,9%,7.29005E+04,4%
+CRC32/poly=Koopman/size=32kB/align=1-8,6.96194E+04,3%,7.42809E+04,3%
+pkg:hash/crc32 goos:darwin goarch:386
+CRC32/poly=IEEE/size=15/align=0-8,,,,,,,6.23500E+01,9%
+CRC32/poly=IEEE/size=15/align=1-8,,,,,,,6.34900E+01,8%
+CRC32/poly=IEEE/size=40/align=0-8,,,,,,,5.74400E+01,3%
+CRC32/poly=IEEE/size=40/align=1-8,,,,,,,5.73300E+01,3%
+CRC32/poly=IEEE/size=512/align=0-8,,,,,,,3.32333E+02,2%
+CRC32/poly=IEEE/size=512/align=1-8,,,,,,,3.35300E+02,3%
+CRC32/poly=IEEE/size=1kB/align=0-8,,,,,,,6.26300E+02,2%
+CRC32/poly=IEEE/size=1kB/align=1-8,,,,,,,6.35300E+02,6%
+CRC32/poly=IEEE/size=4kB/align=0-8,,,,,,,2.45670E+03,7%
+CRC32/poly=IEEE/size=4kB/align=1-8,,,,,,,2.43450E+03,5%
+CRC32/poly=IEEE/size=32kB/align=0-8,,,,,,,1.91499E+04,4%
+CRC32/poly=IEEE/size=32kB/align=1-8,,,,,,,1.94203E+04,5%
+CRC32/poly=Castagnoli/size=15/align=0-8,,,,,,,5.93625E+01,1%
+CRC32/poly=Castagnoli/size=15/align=1-8,,,,,,,5.93500E+01,2%
+CRC32/poly=Castagnoli/size=40/align=0-8,,,,,,,5.96600E+01,6%
+CRC32/poly=Castagnoli/size=40/align=1-8,,,,,,,5.81100E+01,7%
+CRC32/poly=Castagnoli/size=512/align=0-8,,,,,,,3.50800E+02,5%
+CRC32/poly=Castagnoli/size=512/align=1-8,,,,,,,3.49800E+02,7%
+CRC32/poly=Castagnoli/size=1kB/align=0-8,,,,,,,6.56700E+02,4%
+CRC32/poly=Castagnoli/size=1kB/align=1-8,,,,,,,6.56800E+02,6%
+CRC32/poly=Castagnoli/size=4kB/align=0-8,,,,,,,2.45180E+03,4%
+CRC32/poly=Castagnoli/size=4kB/align=1-8,,,,,,,2.44800E+03,5%
+CRC32/poly=Castagnoli/size=32kB/align=0-8,,,,,,,2.05261E+04,5%
+CRC32/poly=Castagnoli/size=32kB/align=1-8,,,,,,,2.01794E+04,9%
+CRC32/poly=Koopman/size=15/align=0-8,,,,,,,5.82100E+01,4%
+CRC32/poly=Koopman/size=15/align=1-8,,,,,,,5.64000E+01,7%
+CRC32/poly=Koopman/size=40/align=0-8,,,,,,,1.42300E+02,8%
+CRC32/poly=Koopman/size=40/align=1-8,,,,,,,1.36222E+02,5%
+CRC32/poly=Koopman/size=512/align=0-8,,,,,,,1.68440E+03,5%
+CRC32/poly=Koopman/size=512/align=1-8,,,,,,,1.64290E+03,4%
+CRC32/poly=Koopman/size=1kB/align=0-8,,,,,,,3.30890E+03,4%
+CRC32/poly=Koopman/size=1kB/align=1-8,,,,,,,3.27920E+03,3%
+CRC32/poly=Koopman/size=4kB/align=0-8,,,,,,,1.33243E+04,3%
+CRC32/poly=Koopman/size=4kB/align=1-8,,,,,,,1.31612E+04,3%
+CRC32/poly=Koopman/size=32kB/align=0-8,,,,,,,1.06891E+05,6%
+CRC32/poly=Koopman/size=32kB/align=1-8,,,,,,,1.06069E+05,4%
+
+[Geo mean],3.44668E+02,,2.37855E+02,,2.39033E+02,,8.74025E+02
+
+name \ speed (MB/s),old.txt,±,new.txt,±,slashslash4.txt,±,x386.txt,±
+pkg:hash/crc32 goos:darwin goarch:amd64
+CRC32/poly=IEEE/size=15/align=0-8,3.20711E+02,8%,3.36950E+02,3%
+CRC32/poly=IEEE/size=15/align=1-8,3.35516E+02,4%,3.37066E+02,4%
+CRC32/poly=IEEE/size=40/align=0-8,9.74718E+02,1%,9.41823E+02,5%,9.50988E+02,3%
+CRC32/poly=IEEE/size=40/align=1-8,9.73636E+02,1%,9.51759E+02,3%,9.59978E+02,4%
+CRC32/poly=IEEE/size=512/align=0-8,2.14703E+03,4%,8.96715E+03,3%
+CRC32/poly=IEEE/size=512/align=1-8,2.16913E+03,3%,8.95606E+03,3%
+CRC32/poly=IEEE/size=1kB/align=0-8,2.26152E+03,4%,1.08807E+04,2%
+CRC32/poly=IEEE/size=1kB/align=1-8,2.30619E+03,2%,1.09768E+04,2%
+CRC32/poly=IEEE/size=4kB/align=0-8,2.35732E+03,7%,1.37258E+04,1%,2.43489E+03,2%
+CRC32/poly=IEEE/size=4kB/align=1-8,2.32511E+03,6%,1.36770E+04,3%,2.42393E+03,4%
+CRC32/poly=IEEE/size=32kB/align=0-8,2.19443E+03,7%,1.51852E+04,3%
+CRC32/poly=IEEE/size=32kB/align=1-8,2.31415E+03,8%,1.50437E+04,3%
+CRC32/poly=Castagnoli/size=15/align=0-8,9.15799E+02,2%,9.20433E+02,2%
+CRC32/poly=Castagnoli/size=15/align=1-8,8.70312E+02,2%,8.67298E+02,2%
+CRC32/poly=Castagnoli/size=40/align=0-8,2.29560E+03,2%,2.28265E+03,4%,2.16074E+03,11%
+CRC32/poly=Castagnoli/size=40/align=1-8,2.03023E+03,3%,2.06346E+03,2%,2.03587E+03,2%
+CRC32/poly=Castagnoli/size=512/align=0-8,1.27437E+04,2%,1.27578E+04,4%
+CRC32/poly=Castagnoli/size=512/align=1-8,1.21445E+04,3%,1.22049E+04,1%
+CRC32/poly=Castagnoli/size=1kB/align=0-8,1.56355E+04,1%,1.54766E+04,1%
+CRC32/poly=Castagnoli/size=1kB/align=1-8,1.46273E+04,6%,1.49597E+04,2%
+CRC32/poly=Castagnoli/size=4kB/align=0-8,2.50862E+04,5%,2.56897E+04,3%,2.53717E+04,7%
+CRC32/poly=Castagnoli/size=4kB/align=1-8,2.41378E+04,6%,2.52736E+04,3%,2.41249E+04,8%
+CRC32/poly=Castagnoli/size=32kB/align=0-8,2.68975E+04,4%,2.68232E+04,5%
+CRC32/poly=Castagnoli/size=32kB/align=1-8,2.59038E+04,3%,2.68422E+04,4%
+CRC32/poly=Koopman/size=15/align=0-8,4.11932E+02,10%,4.21452E+02,3%
+CRC32/poly=Koopman/size=15/align=1-8,4.27408E+02,5%,4.22362E+02,1%
+CRC32/poly=Koopman/size=40/align=0-8,4.36831E+02,9%,4.56472E+02,2%,4.28261E+02,12%
+CRC32/poly=Koopman/size=40/align=1-8,4.39731E+02,6%,4.54515E+02,3%,4.60660E+02,3%
+CRC32/poly=Koopman/size=512/align=0-8,4.52693E+02,5%,4.75749E+02,3%
+CRC32/poly=Koopman/size=512/align=1-8,4.54579E+02,6%,4.39685E+02,8%
+CRC32/poly=Koopman/size=1kB/align=0-8,4.52443E+02,9%,4.37629E+02,4%
+CRC32/poly=Koopman/size=1kB/align=1-8,4.76558E+02,2%,4.34042E+02,5%
+CRC32/poly=Koopman/size=4kB/align=0-8,4.54022E+02,5%,4.55492E+02,6%,4.52301E+02,8%
+CRC32/poly=Koopman/size=4kB/align=1-8,4.59394E+02,9%,4.54627E+02,11%,4.33984E+02,9%
+CRC32/poly=Koopman/size=32kB/align=0-8,4.53471E+02,8%,4.49828E+02,4%
+CRC32/poly=Koopman/size=32kB/align=1-8,4.70784E+02,3%,4.41379E+02,3%
+pkg:hash/crc32 goos:darwin goarch:386
+CRC32/poly=IEEE/size=15/align=0-8,,,,,,,2.41180E+02,8%
+CRC32/poly=IEEE/size=15/align=1-8,,,,,,,2.36728E+02,8%
+CRC32/poly=IEEE/size=40/align=0-8,,,,,,,6.96493E+02,3%
+CRC32/poly=IEEE/size=40/align=1-8,,,,,,,6.97741E+02,3%
+CRC32/poly=IEEE/size=512/align=0-8,,,,,,,1.53793E+03,2%
+CRC32/poly=IEEE/size=512/align=1-8,,,,,,,1.52435E+03,3%
+CRC32/poly=IEEE/size=1kB/align=0-8,,,,,,,1.63354E+03,2%
+CRC32/poly=IEEE/size=1kB/align=1-8,,,,,,,1.61191E+03,6%
+CRC32/poly=IEEE/size=4kB/align=0-8,,,,,,,1.66842E+03,7%
+CRC32/poly=IEEE/size=4kB/align=1-8,,,,,,,1.68370E+03,5%
+CRC32/poly=IEEE/size=32kB/align=0-8,,,,,,,1.71179E+03,4%
+CRC32/poly=IEEE/size=32kB/align=1-8,,,,,,,1.68829E+03,5%
+CRC32/poly=Castagnoli/size=15/align=0-8,,,,,,,2.52761E+02,1%
+CRC32/poly=Castagnoli/size=15/align=1-8,,,,,,,2.52790E+02,2%
+CRC32/poly=Castagnoli/size=40/align=0-8,,,,,,,6.71281E+02,6%
+CRC32/poly=Castagnoli/size=40/align=1-8,,,,,,,6.89073E+02,6%
+CRC32/poly=Castagnoli/size=512/align=0-8,,,,,,,1.45840E+03,5%
+CRC32/poly=Castagnoli/size=512/align=1-8,,,,,,,1.46342E+03,7%
+CRC32/poly=Castagnoli/size=1kB/align=0-8,,,,,,,1.55922E+03,4%
+CRC32/poly=Castagnoli/size=1kB/align=1-8,,,,,,,1.55926E+03,5%
+CRC32/poly=Castagnoli/size=4kB/align=0-8,,,,,,,1.67078E+03,4%
+CRC32/poly=Castagnoli/size=4kB/align=1-8,,,,,,,1.67399E+03,5%
+CRC32/poly=Castagnoli/size=32kB/align=0-8,,,,,,,1.59801E+03,5%
+CRC32/poly=Castagnoli/size=32kB/align=1-8,,,,,,,1.62604E+03,8%
+CRC32/poly=Koopman/size=15/align=0-8,,,,,,,2.57893E+02,5%
+CRC32/poly=Koopman/size=15/align=1-8,,,,,,,2.66301E+02,6%
+CRC32/poly=Koopman/size=40/align=0-8,,,,,,,2.80521E+02,7%
+CRC32/poly=Koopman/size=40/align=1-8,,,,,,,2.89866E+02,8%
+CRC32/poly=Koopman/size=512/align=0-8,,,,,,,3.04127E+02,5%
+CRC32/poly=Koopman/size=512/align=1-8,,,,,,,3.11609E+02,3%
+CRC32/poly=Koopman/size=1kB/align=0-8,,,,,,,3.09621E+02,4%
+CRC32/poly=Koopman/size=1kB/align=1-8,,,,,,,3.12318E+02,3%
+CRC32/poly=Koopman/size=4kB/align=0-8,,,,,,,3.07516E+02,3%
+CRC32/poly=Koopman/size=4kB/align=1-8,,,,,,,3.11292E+02,3%
+CRC32/poly=Koopman/size=32kB/align=0-8,,,,,,,3.06816E+02,6%
+CRC32/poly=Koopman/size=32kB/align=1-8,,,,,,,3.09085E+02,4%
+
+[Geo mean],1.71221E+03,,2.48066E+03,,1.69486E+03,,6.75103E+02
diff --git a/cmd/benchstat/testdata/allnorangecsv.golden b/cmd/benchstat/testdata/allnorangecsv.golden
new file mode 100644
index 0000000..3916a37
--- /dev/null
+++ b/cmd/benchstat/testdata/allnorangecsv.golden
@@ -0,0 +1,155 @@
+name \ time/op (ns/op),./old.txt,./new.txt,slashslash4.txt,x386.txt
+pkg:hash/crc32 goos:darwin goarch:amd64
+CRC32/poly=IEEE/size=15/align=0-8,4.68700E+01,4.45200E+01
+CRC32/poly=IEEE/size=15/align=1-8,4.47100E+01,4.45000E+01
+CRC32/poly=IEEE/size=40/align=0-8,4.10375E+01,4.25000E+01,4.20800E+01
+CRC32/poly=IEEE/size=40/align=1-8,4.10778E+01,4.20400E+01,4.16800E+01
+CRC32/poly=IEEE/size=512/align=0-8,2.38000E+02,5.71200E+01
+CRC32/poly=IEEE/size=512/align=1-8,2.35500E+02,5.71700E+01
+CRC32/poly=IEEE/size=1kB/align=0-8,4.52500E+02,9.41125E+01
+CRC32/poly=IEEE/size=1kB/align=1-8,4.43600E+02,9.32875E+01
+CRC32/poly=IEEE/size=4kB/align=0-8,1.74000E+03,2.98111E+02,1.68200E+03
+CRC32/poly=IEEE/size=4kB/align=1-8,1.76430E+03,2.99100E+02,1.68980E+03
+CRC32/poly=IEEE/size=32kB/align=0-8,1.49529E+04,2.15800E+03
+CRC32/poly=IEEE/size=32kB/align=1-8,1.41888E+04,2.17830E+03
+CRC32/poly=Castagnoli/size=15/align=0-8,1.63778E+01,1.63000E+01
+CRC32/poly=Castagnoli/size=15/align=1-8,1.72222E+01,1.72900E+01
+CRC32/poly=Castagnoli/size=40/align=0-8,1.74300E+01,1.75300E+01,1.85800E+01
+CRC32/poly=Castagnoli/size=40/align=1-8,1.97100E+01,1.93900E+01,1.96375E+01
+CRC32/poly=Castagnoli/size=512/align=0-8,4.01700E+01,4.01300E+01
+CRC32/poly=Castagnoli/size=512/align=1-8,4.21400E+01,4.19444E+01
+CRC32/poly=Castagnoli/size=1kB/align=0-8,6.55000E+01,6.61625E+01
+CRC32/poly=Castagnoli/size=1kB/align=1-8,7.00900E+01,6.84667E+01
+CRC32/poly=Castagnoli/size=4kB/align=0-8,1.62800E+02,1.58800E+02,1.61200E+02
+CRC32/poly=Castagnoli/size=4kB/align=1-8,1.69400E+02,1.61600E+02,1.69600E+02
+CRC32/poly=Castagnoli/size=32kB/align=0-8,1.21822E+03,1.21433E+03
+CRC32/poly=Castagnoli/size=32kB/align=1-8,1.26478E+03,1.22080E+03
+CRC32/poly=Koopman/size=15/align=0-8,3.65100E+01,3.56000E+01
+CRC32/poly=Koopman/size=15/align=1-8,3.51500E+01,3.55111E+01
+CRC32/poly=Koopman/size=40/align=0-8,9.16400E+01,8.76500E+01,9.38100E+01
+CRC32/poly=Koopman/size=40/align=1-8,9.10800E+01,8.80300E+01,8.68600E+01
+CRC32/poly=Koopman/size=512/align=0-8,1.13170E+03,1.07590E+03
+CRC32/poly=Koopman/size=512/align=1-8,1.12680E+03,1.16660E+03
+CRC32/poly=Koopman/size=1kB/align=0-8,2.24333E+03,2.34070E+03
+CRC32/poly=Koopman/size=1kB/align=1-8,2.14867E+03,2.36010E+03
+CRC32/poly=Koopman/size=4kB/align=0-8,9.03150E+03,9.00320E+03,9.08160E+03
+CRC32/poly=Koopman/size=4kB/align=1-8,8.94020E+03,9.04630E+03,9.45520E+03
+CRC32/poly=Koopman/size=32kB/align=0-8,7.24280E+04,7.29005E+04
+CRC32/poly=Koopman/size=32kB/align=1-8,6.96194E+04,7.42809E+04
+pkg:hash/crc32 goos:darwin goarch:386
+CRC32/poly=IEEE/size=15/align=0-8,,,,6.23500E+01
+CRC32/poly=IEEE/size=15/align=1-8,,,,6.34900E+01
+CRC32/poly=IEEE/size=40/align=0-8,,,,5.74400E+01
+CRC32/poly=IEEE/size=40/align=1-8,,,,5.73300E+01
+CRC32/poly=IEEE/size=512/align=0-8,,,,3.32333E+02
+CRC32/poly=IEEE/size=512/align=1-8,,,,3.35300E+02
+CRC32/poly=IEEE/size=1kB/align=0-8,,,,6.26300E+02
+CRC32/poly=IEEE/size=1kB/align=1-8,,,,6.35300E+02
+CRC32/poly=IEEE/size=4kB/align=0-8,,,,2.45670E+03
+CRC32/poly=IEEE/size=4kB/align=1-8,,,,2.43450E+03
+CRC32/poly=IEEE/size=32kB/align=0-8,,,,1.91499E+04
+CRC32/poly=IEEE/size=32kB/align=1-8,,,,1.94203E+04
+CRC32/poly=Castagnoli/size=15/align=0-8,,,,5.93625E+01
+CRC32/poly=Castagnoli/size=15/align=1-8,,,,5.93500E+01
+CRC32/poly=Castagnoli/size=40/align=0-8,,,,5.96600E+01
+CRC32/poly=Castagnoli/size=40/align=1-8,,,,5.81100E+01
+CRC32/poly=Castagnoli/size=512/align=0-8,,,,3.50800E+02
+CRC32/poly=Castagnoli/size=512/align=1-8,,,,3.49800E+02
+CRC32/poly=Castagnoli/size=1kB/align=0-8,,,,6.56700E+02
+CRC32/poly=Castagnoli/size=1kB/align=1-8,,,,6.56800E+02
+CRC32/poly=Castagnoli/size=4kB/align=0-8,,,,2.45180E+03
+CRC32/poly=Castagnoli/size=4kB/align=1-8,,,,2.44800E+03
+CRC32/poly=Castagnoli/size=32kB/align=0-8,,,,2.05261E+04
+CRC32/poly=Castagnoli/size=32kB/align=1-8,,,,2.01794E+04
+CRC32/poly=Koopman/size=15/align=0-8,,,,5.82100E+01
+CRC32/poly=Koopman/size=15/align=1-8,,,,5.64000E+01
+CRC32/poly=Koopman/size=40/align=0-8,,,,1.42300E+02
+CRC32/poly=Koopman/size=40/align=1-8,,,,1.36222E+02
+CRC32/poly=Koopman/size=512/align=0-8,,,,1.68440E+03
+CRC32/poly=Koopman/size=512/align=1-8,,,,1.64290E+03
+CRC32/poly=Koopman/size=1kB/align=0-8,,,,3.30890E+03
+CRC32/poly=Koopman/size=1kB/align=1-8,,,,3.27920E+03
+CRC32/poly=Koopman/size=4kB/align=0-8,,,,1.33243E+04
+CRC32/poly=Koopman/size=4kB/align=1-8,,,,1.31612E+04
+CRC32/poly=Koopman/size=32kB/align=0-8,,,,1.06891E+05
+CRC32/poly=Koopman/size=32kB/align=1-8,,,,1.06069E+05
+
+[Geo mean],3.44668E+02,2.37855E+02,2.39033E+02,8.74025E+02
+
+name \ speed (MB/s),./old.txt,./new.txt,slashslash4.txt,x386.txt
+pkg:hash/crc32 goos:darwin goarch:amd64
+CRC32/poly=IEEE/size=15/align=0-8,3.20711E+02,3.36950E+02
+CRC32/poly=IEEE/size=15/align=1-8,3.35516E+02,3.37066E+02
+CRC32/poly=IEEE/size=40/align=0-8,9.74718E+02,9.41823E+02,9.50988E+02
+CRC32/poly=IEEE/size=40/align=1-8,9.73636E+02,9.51759E+02,9.59978E+02
+CRC32/poly=IEEE/size=512/align=0-8,2.14703E+03,8.96715E+03
+CRC32/poly=IEEE/size=512/align=1-8,2.16913E+03,8.95606E+03
+CRC32/poly=IEEE/size=1kB/align=0-8,2.26152E+03,1.08807E+04
+CRC32/poly=IEEE/size=1kB/align=1-8,2.30619E+03,1.09768E+04
+CRC32/poly=IEEE/size=4kB/align=0-8,2.35732E+03,1.37258E+04,2.43489E+03
+CRC32/poly=IEEE/size=4kB/align=1-8,2.32511E+03,1.36770E+04,2.42393E+03
+CRC32/poly=IEEE/size=32kB/align=0-8,2.19443E+03,1.51852E+04
+CRC32/poly=IEEE/size=32kB/align=1-8,2.31415E+03,1.50437E+04
+CRC32/poly=Castagnoli/size=15/align=0-8,9.15799E+02,9.20433E+02
+CRC32/poly=Castagnoli/size=15/align=1-8,8.70312E+02,8.67298E+02
+CRC32/poly=Castagnoli/size=40/align=0-8,2.29560E+03,2.28265E+03,2.16074E+03
+CRC32/poly=Castagnoli/size=40/align=1-8,2.03023E+03,2.06346E+03,2.03587E+03
+CRC32/poly=Castagnoli/size=512/align=0-8,1.27437E+04,1.27578E+04
+CRC32/poly=Castagnoli/size=512/align=1-8,1.21445E+04,1.22049E+04
+CRC32/poly=Castagnoli/size=1kB/align=0-8,1.56355E+04,1.54766E+04
+CRC32/poly=Castagnoli/size=1kB/align=1-8,1.46273E+04,1.49597E+04
+CRC32/poly=Castagnoli/size=4kB/align=0-8,2.50862E+04,2.56897E+04,2.53717E+04
+CRC32/poly=Castagnoli/size=4kB/align=1-8,2.41378E+04,2.52736E+04,2.41249E+04
+CRC32/poly=Castagnoli/size=32kB/align=0-8,2.68975E+04,2.68232E+04
+CRC32/poly=Castagnoli/size=32kB/align=1-8,2.59038E+04,2.68422E+04
+CRC32/poly=Koopman/size=15/align=0-8,4.11932E+02,4.21452E+02
+CRC32/poly=Koopman/size=15/align=1-8,4.27408E+02,4.22362E+02
+CRC32/poly=Koopman/size=40/align=0-8,4.36831E+02,4.56472E+02,4.28261E+02
+CRC32/poly=Koopman/size=40/align=1-8,4.39731E+02,4.54515E+02,4.60660E+02
+CRC32/poly=Koopman/size=512/align=0-8,4.52693E+02,4.75749E+02
+CRC32/poly=Koopman/size=512/align=1-8,4.54579E+02,4.39685E+02
+CRC32/poly=Koopman/size=1kB/align=0-8,4.52443E+02,4.37629E+02
+CRC32/poly=Koopman/size=1kB/align=1-8,4.76558E+02,4.34042E+02
+CRC32/poly=Koopman/size=4kB/align=0-8,4.54022E+02,4.55492E+02,4.52301E+02
+CRC32/poly=Koopman/size=4kB/align=1-8,4.59394E+02,4.54627E+02,4.33984E+02
+CRC32/poly=Koopman/size=32kB/align=0-8,4.53471E+02,4.49828E+02
+CRC32/poly=Koopman/size=32kB/align=1-8,4.70784E+02,4.41379E+02
+pkg:hash/crc32 goos:darwin goarch:386
+CRC32/poly=IEEE/size=15/align=0-8,,,,2.41180E+02
+CRC32/poly=IEEE/size=15/align=1-8,,,,2.36728E+02
+CRC32/poly=IEEE/size=40/align=0-8,,,,6.96493E+02
+CRC32/poly=IEEE/size=40/align=1-8,,,,6.97741E+02
+CRC32/poly=IEEE/size=512/align=0-8,,,,1.53793E+03
+CRC32/poly=IEEE/size=512/align=1-8,,,,1.52435E+03
+CRC32/poly=IEEE/size=1kB/align=0-8,,,,1.63354E+03
+CRC32/poly=IEEE/size=1kB/align=1-8,,,,1.61191E+03
+CRC32/poly=IEEE/size=4kB/align=0-8,,,,1.66842E+03
+CRC32/poly=IEEE/size=4kB/align=1-8,,,,1.68370E+03
+CRC32/poly=IEEE/size=32kB/align=0-8,,,,1.71179E+03
+CRC32/poly=IEEE/size=32kB/align=1-8,,,,1.68829E+03
+CRC32/poly=Castagnoli/size=15/align=0-8,,,,2.52761E+02
+CRC32/poly=Castagnoli/size=15/align=1-8,,,,2.52790E+02
+CRC32/poly=Castagnoli/size=40/align=0-8,,,,6.71281E+02
+CRC32/poly=Castagnoli/size=40/align=1-8,,,,6.89073E+02
+CRC32/poly=Castagnoli/size=512/align=0-8,,,,1.45840E+03
+CRC32/poly=Castagnoli/size=512/align=1-8,,,,1.46342E+03
+CRC32/poly=Castagnoli/size=1kB/align=0-8,,,,1.55922E+03
+CRC32/poly=Castagnoli/size=1kB/align=1-8,,,,1.55926E+03
+CRC32/poly=Castagnoli/size=4kB/align=0-8,,,,1.67078E+03
+CRC32/poly=Castagnoli/size=4kB/align=1-8,,,,1.67399E+03
+CRC32/poly=Castagnoli/size=32kB/align=0-8,,,,1.59801E+03
+CRC32/poly=Castagnoli/size=32kB/align=1-8,,,,1.62604E+03
+CRC32/poly=Koopman/size=15/align=0-8,,,,2.57893E+02
+CRC32/poly=Koopman/size=15/align=1-8,,,,2.66301E+02
+CRC32/poly=Koopman/size=40/align=0-8,,,,2.80521E+02
+CRC32/poly=Koopman/size=40/align=1-8,,,,2.89866E+02
+CRC32/poly=Koopman/size=512/align=0-8,,,,3.04127E+02
+CRC32/poly=Koopman/size=512/align=1-8,,,,3.11609E+02
+CRC32/poly=Koopman/size=1kB/align=0-8,,,,3.09621E+02
+CRC32/poly=Koopman/size=1kB/align=1-8,,,,3.12318E+02
+CRC32/poly=Koopman/size=4kB/align=0-8,,,,3.07516E+02
+CRC32/poly=Koopman/size=4kB/align=1-8,,,,3.11292E+02
+CRC32/poly=Koopman/size=32kB/align=0-8,,,,3.06816E+02
+CRC32/poly=Koopman/size=32kB/align=1-8,,,,3.09085E+02
+
+[Geo mean],1.71221E+03,2.48066E+03,1.69486E+03,6.75103E+02
diff --git a/cmd/benchstat/testdata/allnosplitcsv.golden b/cmd/benchstat/testdata/allnosplitcsv.golden
new file mode 100644
index 0000000..b3513a4
--- /dev/null
+++ b/cmd/benchstat/testdata/allnosplitcsv.golden
@@ -0,0 +1,77 @@
+name \ time/op (ns/op),new.txt,±,old.txt,±,slashslash4.txt,±,x386.txt,±
+CRC32/poly=IEEE/size=15/align=0-8,4.45200E+01,3%,4.68700E+01,8%,,,6.23500E+01,9%
+CRC32/poly=IEEE/size=15/align=1-8,4.45000E+01,4%,4.47100E+01,5%,,,6.34900E+01,8%
+CRC32/poly=IEEE/size=40/align=0-8,4.25000E+01,6%,4.10375E+01,1%,4.20800E+01,3%,5.74400E+01,3%
+CRC32/poly=IEEE/size=40/align=1-8,4.20400E+01,3%,4.10778E+01,1%,4.16800E+01,5%,5.73300E+01,3%
+CRC32/poly=IEEE/size=512/align=0-8,5.71200E+01,3%,2.38000E+02,5%,,,3.32333E+02,2%
+CRC32/poly=IEEE/size=512/align=1-8,5.71700E+01,3%,2.35500E+02,3%,,,3.35300E+02,3%
+CRC32/poly=IEEE/size=1kB/align=0-8,9.41125E+01,2%,4.52500E+02,4%,,,6.26300E+02,2%
+CRC32/poly=IEEE/size=1kB/align=1-8,9.32875E+01,2%,4.43600E+02,2%,,,6.35300E+02,6%
+CRC32/poly=IEEE/size=4kB/align=0-8,2.98111E+02,1%,1.74000E+03,8%,1.68200E+03,2%,2.45670E+03,7%
+CRC32/poly=IEEE/size=4kB/align=1-8,2.99100E+02,3%,1.76430E+03,6%,1.68980E+03,4%,2.43450E+03,5%
+CRC32/poly=IEEE/size=32kB/align=0-8,2.15800E+03,3%,1.49529E+04,7%,,,1.91499E+04,4%
+CRC32/poly=IEEE/size=32kB/align=1-8,2.17830E+03,3%,1.41888E+04,7%,,,1.94203E+04,5%
+CRC32/poly=Castagnoli/size=15/align=0-8,1.63000E+01,2%,1.63778E+01,3%,,,5.93625E+01,1%
+CRC32/poly=Castagnoli/size=15/align=1-8,1.72900E+01,2%,1.72222E+01,2%,,,5.93500E+01,2%
+CRC32/poly=Castagnoli/size=40/align=0-8,1.75300E+01,4%,1.74300E+01,2%,1.85800E+01,11%,5.96600E+01,6%
+CRC32/poly=Castagnoli/size=40/align=1-8,1.93900E+01,2%,1.97100E+01,3%,1.96375E+01,2%,5.81100E+01,7%
+CRC32/poly=Castagnoli/size=512/align=0-8,4.01300E+01,4%,4.01700E+01,2%,,,3.50800E+02,5%
+CRC32/poly=Castagnoli/size=512/align=1-8,4.19444E+01,2%,4.21400E+01,3%,,,3.49800E+02,7%
+CRC32/poly=Castagnoli/size=1kB/align=0-8,6.61625E+01,1%,6.55000E+01,1%,,,6.56700E+02,4%
+CRC32/poly=Castagnoli/size=1kB/align=1-8,6.84667E+01,2%,7.00900E+01,6%,,,6.56800E+02,6%
+CRC32/poly=Castagnoli/size=4kB/align=0-8,1.58800E+02,3%,1.62800E+02,5%,1.61200E+02,8%,2.45180E+03,4%
+CRC32/poly=Castagnoli/size=4kB/align=1-8,1.61600E+02,3%,1.69400E+02,6%,1.69600E+02,8%,2.44800E+03,5%
+CRC32/poly=Castagnoli/size=32kB/align=0-8,1.21433E+03,3%,1.21822E+03,4%,,,2.05261E+04,5%
+CRC32/poly=Castagnoli/size=32kB/align=1-8,1.22080E+03,4%,1.26478E+03,3%,,,2.01794E+04,9%
+CRC32/poly=Koopman/size=15/align=0-8,3.56000E+01,3%,3.65100E+01,11%,,,5.82100E+01,4%
+CRC32/poly=Koopman/size=15/align=1-8,3.55111E+01,1%,3.51500E+01,5%,,,5.64000E+01,7%
+CRC32/poly=Koopman/size=40/align=0-8,8.76500E+01,2%,9.16400E+01,9%,9.38100E+01,13%,1.42300E+02,8%
+CRC32/poly=Koopman/size=40/align=1-8,8.80300E+01,3%,9.10800E+01,6%,8.68600E+01,3%,1.36222E+02,5%
+CRC32/poly=Koopman/size=512/align=0-8,1.07590E+03,3%,1.13170E+03,5%,,,1.68440E+03,5%
+CRC32/poly=Koopman/size=512/align=1-8,1.16660E+03,8%,1.12680E+03,6%,,,1.64290E+03,4%
+CRC32/poly=Koopman/size=1kB/align=0-8,2.34070E+03,4%,2.24333E+03,6%,,,3.30890E+03,4%
+CRC32/poly=Koopman/size=1kB/align=1-8,2.36010E+03,5%,2.14867E+03,2%,,,3.27920E+03,3%
+CRC32/poly=Koopman/size=4kB/align=0-8,9.00320E+03,6%,9.03150E+03,6%,9.08160E+03,8%,1.33243E+04,3%
+CRC32/poly=Koopman/size=4kB/align=1-8,9.04630E+03,12%,8.94020E+03,10%,9.45520E+03,8%,1.31612E+04,3%
+CRC32/poly=Koopman/size=32kB/align=0-8,7.29005E+04,4%,7.24280E+04,9%,,,1.06891E+05,6%
+CRC32/poly=Koopman/size=32kB/align=1-8,7.42809E+04,3%,6.96194E+04,3%,,,1.06069E+05,4%
+[Geo mean],2.37855E+02,,3.44668E+02,,2.39033E+02,,8.74025E+02
+
+name \ speed (MB/s),new.txt,±,old.txt,±,slashslash4.txt,±,x386.txt,±
+CRC32/poly=IEEE/size=15/align=0-8,3.36950E+02,3%,3.20711E+02,8%,,,2.41180E+02,8%
+CRC32/poly=IEEE/size=15/align=1-8,3.37066E+02,4%,3.35516E+02,4%,,,2.36728E+02,8%
+CRC32/poly=IEEE/size=40/align=0-8,9.41823E+02,5%,9.74718E+02,1%,9.50988E+02,3%,6.96493E+02,3%
+CRC32/poly=IEEE/size=40/align=1-8,9.51759E+02,3%,9.73636E+02,1%,9.59978E+02,4%,6.97741E+02,3%
+CRC32/poly=IEEE/size=512/align=0-8,8.96715E+03,3%,2.14703E+03,4%,,,1.53793E+03,2%
+CRC32/poly=IEEE/size=512/align=1-8,8.95606E+03,3%,2.16913E+03,3%,,,1.52435E+03,3%
+CRC32/poly=IEEE/size=1kB/align=0-8,1.08807E+04,2%,2.26152E+03,4%,,,1.63354E+03,2%
+CRC32/poly=IEEE/size=1kB/align=1-8,1.09768E+04,2%,2.30619E+03,2%,,,1.61191E+03,6%
+CRC32/poly=IEEE/size=4kB/align=0-8,1.37258E+04,1%,2.35732E+03,7%,2.43489E+03,2%,1.66842E+03,7%
+CRC32/poly=IEEE/size=4kB/align=1-8,1.36770E+04,3%,2.32511E+03,6%,2.42393E+03,4%,1.68370E+03,5%
+CRC32/poly=IEEE/size=32kB/align=0-8,1.51852E+04,3%,2.19443E+03,7%,,,1.71179E+03,4%
+CRC32/poly=IEEE/size=32kB/align=1-8,1.50437E+04,3%,2.31415E+03,8%,,,1.68829E+03,5%
+CRC32/poly=Castagnoli/size=15/align=0-8,9.20433E+02,2%,9.15799E+02,2%,,,2.52761E+02,1%
+CRC32/poly=Castagnoli/size=15/align=1-8,8.67298E+02,2%,8.70312E+02,2%,,,2.52790E+02,2%
+CRC32/poly=Castagnoli/size=40/align=0-8,2.28265E+03,4%,2.29560E+03,2%,2.16074E+03,11%,6.71281E+02,6%
+CRC32/poly=Castagnoli/size=40/align=1-8,2.06346E+03,2%,2.03023E+03,3%,2.03587E+03,2%,6.89073E+02,6%
+CRC32/poly=Castagnoli/size=512/align=0-8,1.27578E+04,4%,1.27437E+04,2%,,,1.45840E+03,5%
+CRC32/poly=Castagnoli/size=512/align=1-8,1.22049E+04,1%,1.21445E+04,3%,,,1.46342E+03,7%
+CRC32/poly=Castagnoli/size=1kB/align=0-8,1.54766E+04,1%,1.56355E+04,1%,,,1.55922E+03,4%
+CRC32/poly=Castagnoli/size=1kB/align=1-8,1.49597E+04,2%,1.46273E+04,6%,,,1.55926E+03,5%
+CRC32/poly=Castagnoli/size=4kB/align=0-8,2.56897E+04,3%,2.50862E+04,5%,2.53717E+04,7%,1.67078E+03,4%
+CRC32/poly=Castagnoli/size=4kB/align=1-8,2.52736E+04,3%,2.41378E+04,6%,2.41249E+04,8%,1.67399E+03,5%
+CRC32/poly=Castagnoli/size=32kB/align=0-8,2.68232E+04,5%,2.68975E+04,4%,,,1.59801E+03,5%
+CRC32/poly=Castagnoli/size=32kB/align=1-8,2.68422E+04,4%,2.59038E+04,3%,,,1.62604E+03,8%
+CRC32/poly=Koopman/size=15/align=0-8,4.21452E+02,3%,4.11932E+02,10%,,,2.57893E+02,5%
+CRC32/poly=Koopman/size=15/align=1-8,4.22362E+02,1%,4.27408E+02,5%,,,2.66301E+02,6%
+CRC32/poly=Koopman/size=40/align=0-8,4.56472E+02,2%,4.36831E+02,9%,4.28261E+02,12%,2.80521E+02,7%
+CRC32/poly=Koopman/size=40/align=1-8,4.54515E+02,3%,4.39731E+02,6%,4.60660E+02,3%,2.89866E+02,8%
+CRC32/poly=Koopman/size=512/align=0-8,4.75749E+02,3%,4.52693E+02,5%,,,3.04127E+02,5%
+CRC32/poly=Koopman/size=512/align=1-8,4.39685E+02,8%,4.54579E+02,6%,,,3.11609E+02,3%
+CRC32/poly=Koopman/size=1kB/align=0-8,4.37629E+02,4%,4.52443E+02,9%,,,3.09621E+02,4%
+CRC32/poly=Koopman/size=1kB/align=1-8,4.34042E+02,5%,4.76558E+02,2%,,,3.12318E+02,3%
+CRC32/poly=Koopman/size=4kB/align=0-8,4.55492E+02,6%,4.54022E+02,5%,4.52301E+02,8%,3.07516E+02,3%
+CRC32/poly=Koopman/size=4kB/align=1-8,4.54627E+02,11%,4.59394E+02,9%,4.33984E+02,9%,3.11292E+02,3%
+CRC32/poly=Koopman/size=32kB/align=0-8,4.49828E+02,4%,4.53471E+02,8%,,,3.06816E+02,6%
+CRC32/poly=Koopman/size=32kB/align=1-8,4.41379E+02,3%,4.70784E+02,3%,,,3.09085E+02,4%
+[Geo mean],2.48066E+03,,1.71221E+03,,1.69486E+03,,6.75103E+02
diff --git a/cmd/benchstat/testdata/oldcsv.golden b/cmd/benchstat/testdata/oldcsv.golden
new file mode 100644
index 0000000..3e271a2
--- /dev/null
+++ b/cmd/benchstat/testdata/oldcsv.golden
@@ -0,0 +1,77 @@
+name,time/op (ns/op),±
+CRC32/poly=IEEE/size=15/align=0-8,4.68700E+01,8%
+CRC32/poly=IEEE/size=15/align=1-8,4.47100E+01,5%
+CRC32/poly=IEEE/size=40/align=0-8,4.10375E+01,1%
+CRC32/poly=IEEE/size=40/align=1-8,4.10778E+01,1%
+CRC32/poly=IEEE/size=512/align=0-8,2.38000E+02,5%
+CRC32/poly=IEEE/size=512/align=1-8,2.35500E+02,3%
+CRC32/poly=IEEE/size=1kB/align=0-8,4.52500E+02,4%
+CRC32/poly=IEEE/size=1kB/align=1-8,4.43600E+02,2%
+CRC32/poly=IEEE/size=4kB/align=0-8,1.74000E+03,8%
+CRC32/poly=IEEE/size=4kB/align=1-8,1.76430E+03,6%
+CRC32/poly=IEEE/size=32kB/align=0-8,1.49529E+04,7%
+CRC32/poly=IEEE/size=32kB/align=1-8,1.41888E+04,7%
+CRC32/poly=Castagnoli/size=15/align=0-8,1.63778E+01,3%
+CRC32/poly=Castagnoli/size=15/align=1-8,1.72222E+01,2%
+CRC32/poly=Castagnoli/size=40/align=0-8,1.74300E+01,2%
+CRC32/poly=Castagnoli/size=40/align=1-8,1.97100E+01,3%
+CRC32/poly=Castagnoli/size=512/align=0-8,4.01700E+01,2%
+CRC32/poly=Castagnoli/size=512/align=1-8,4.21400E+01,3%
+CRC32/poly=Castagnoli/size=1kB/align=0-8,6.55000E+01,1%
+CRC32/poly=Castagnoli/size=1kB/align=1-8,7.00900E+01,6%
+CRC32/poly=Castagnoli/size=4kB/align=0-8,1.62800E+02,5%
+CRC32/poly=Castagnoli/size=4kB/align=1-8,1.69400E+02,6%
+CRC32/poly=Castagnoli/size=32kB/align=0-8,1.21822E+03,4%
+CRC32/poly=Castagnoli/size=32kB/align=1-8,1.26478E+03,3%
+CRC32/poly=Koopman/size=15/align=0-8,3.65100E+01,11%
+CRC32/poly=Koopman/size=15/align=1-8,3.51500E+01,5%
+CRC32/poly=Koopman/size=40/align=0-8,9.16400E+01,9%
+CRC32/poly=Koopman/size=40/align=1-8,9.10800E+01,6%
+CRC32/poly=Koopman/size=512/align=0-8,1.13170E+03,5%
+CRC32/poly=Koopman/size=512/align=1-8,1.12680E+03,6%
+CRC32/poly=Koopman/size=1kB/align=0-8,2.24333E+03,6%
+CRC32/poly=Koopman/size=1kB/align=1-8,2.14867E+03,2%
+CRC32/poly=Koopman/size=4kB/align=0-8,9.03150E+03,6%
+CRC32/poly=Koopman/size=4kB/align=1-8,8.94020E+03,10%
+CRC32/poly=Koopman/size=32kB/align=0-8,7.24280E+04,9%
+CRC32/poly=Koopman/size=32kB/align=1-8,6.96194E+04,3%
+[Geo mean],3.44668E+02
+
+name,speed (MB/s),±
+CRC32/poly=IEEE/size=15/align=0-8,3.20711E+02,8%
+CRC32/poly=IEEE/size=15/align=1-8,3.35516E+02,4%
+CRC32/poly=IEEE/size=40/align=0-8,9.74718E+02,1%
+CRC32/poly=IEEE/size=40/align=1-8,9.73636E+02,1%
+CRC32/poly=IEEE/size=512/align=0-8,2.14703E+03,4%
+CRC32/poly=IEEE/size=512/align=1-8,2.16913E+03,3%
+CRC32/poly=IEEE/size=1kB/align=0-8,2.26152E+03,4%
+CRC32/poly=IEEE/size=1kB/align=1-8,2.30619E+03,2%
+CRC32/poly=IEEE/size=4kB/align=0-8,2.35732E+03,7%
+CRC32/poly=IEEE/size=4kB/align=1-8,2.32511E+03,6%
+CRC32/poly=IEEE/size=32kB/align=0-8,2.19443E+03,7%
+CRC32/poly=IEEE/size=32kB/align=1-8,2.31415E+03,8%
+CRC32/poly=Castagnoli/size=15/align=0-8,9.15799E+02,2%
+CRC32/poly=Castagnoli/size=15/align=1-8,8.70312E+02,2%
+CRC32/poly=Castagnoli/size=40/align=0-8,2.29560E+03,2%
+CRC32/poly=Castagnoli/size=40/align=1-8,2.03023E+03,3%
+CRC32/poly=Castagnoli/size=512/align=0-8,1.27437E+04,2%
+CRC32/poly=Castagnoli/size=512/align=1-8,1.21445E+04,3%
+CRC32/poly=Castagnoli/size=1kB/align=0-8,1.56355E+04,1%
+CRC32/poly=Castagnoli/size=1kB/align=1-8,1.46273E+04,6%
+CRC32/poly=Castagnoli/size=4kB/align=0-8,2.50862E+04,5%
+CRC32/poly=Castagnoli/size=4kB/align=1-8,2.41378E+04,6%
+CRC32/poly=Castagnoli/size=32kB/align=0-8,2.68975E+04,4%
+CRC32/poly=Castagnoli/size=32kB/align=1-8,2.59038E+04,3%
+CRC32/poly=Koopman/size=15/align=0-8,4.11932E+02,10%
+CRC32/poly=Koopman/size=15/align=1-8,4.27408E+02,5%
+CRC32/poly=Koopman/size=40/align=0-8,4.36831E+02,9%
+CRC32/poly=Koopman/size=40/align=1-8,4.39731E+02,6%
+CRC32/poly=Koopman/size=512/align=0-8,4.52693E+02,5%
+CRC32/poly=Koopman/size=512/align=1-8,4.54579E+02,6%
+CRC32/poly=Koopman/size=1kB/align=0-8,4.52443E+02,9%
+CRC32/poly=Koopman/size=1kB/align=1-8,4.76558E+02,2%
+CRC32/poly=Koopman/size=4kB/align=0-8,4.54022E+02,5%
+CRC32/poly=Koopman/size=4kB/align=1-8,4.59394E+02,9%
+CRC32/poly=Koopman/size=32kB/align=0-8,4.53471E+02,8%
+CRC32/poly=Koopman/size=32kB/align=1-8,4.70784E+02,3%
+[Geo mean],1.71221E+03