widths []int // list of column widths in runes - re-used during formatting
}
-func (b *Writer) addLine() { b.lines = append(b.lines, []cell{}) }
+func (b *Writer) addLine() {
+ // Grow slice instead of appending,
+ // as that gives us an opportunity
+ // to re-use an existing []cell.
+ if n := len(b.lines) + 1; n <= cap(b.lines) {
+ b.lines = b.lines[:n]
+ b.lines[n-1] = b.lines[n-1][:0]
+ } else {
+ b.lines = append(b.lines, nil)
+ }
+
+ // The previous line is probably a good indicator
+ // of how many cells the current line will have.
+ // If the current line's capacity is smaller than that,
+ // abandon it and make a new one.
+ if n := len(b.lines); n >= 2 {
+ if prev := len(b.lines[n-2]); prev > cap(b.lines[n-1]) {
+ b.lines[n-1] = make([]cell, 0, prev)
+ }
+ }
+}
// Reset the current state.
func (b *Writer) reset() {
package tabwriter_test
import (
+ "bytes"
+ "fmt"
"io"
+ "io/ioutil"
"testing"
. "text/tabwriter"
)
io.WriteString(w, "a\n\n") // the second \n triggers a call to w.Write and thus a panic
t.Errorf("failed to panic during Write")
}
+
+func BenchmarkTable(b *testing.B) {
+ for _, w := range [...]int{1, 10, 100} {
+ // Build a line with w cells.
+ line := bytes.Repeat([]byte("a\t"), w)
+ line = append(line, '\n')
+ for _, h := range [...]int{10, 1000, 100000} {
+ b.Run(fmt.Sprintf("%dx%d", w, h), func(b *testing.B) {
+ b.Run("new", func(b *testing.B) {
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ w := NewWriter(ioutil.Discard, 4, 4, 1, ' ', 0) // no particular reason for these settings
+ // Write the line h times.
+ for j := 0; j < h; j++ {
+ w.Write(line)
+ }
+ w.Flush()
+ }
+ })
+
+ b.Run("reuse", func(b *testing.B) {
+ b.ReportAllocs()
+ w := NewWriter(ioutil.Discard, 4, 4, 1, ' ', 0) // no particular reason for these settings
+ for i := 0; i < b.N; i++ {
+ // Write the line h times.
+ for j := 0; j < h; j++ {
+ w.Write(line)
+ }
+ w.Flush()
+ }
+ })
+ })
+ }
+ }
+}
+
+func BenchmarkPyramid(b *testing.B) {
+ for _, x := range [...]int{10, 100, 1000} {
+ // Build a line with x cells.
+ line := bytes.Repeat([]byte("a\t"), x)
+ line = append(line, '\n')
+ b.Run(fmt.Sprintf("%d", x), func(b *testing.B) {
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ w := NewWriter(ioutil.Discard, 4, 4, 1, ' ', 0) // no particular reason for these settings
+ // Write increasing prefixes of that line.
+ for j := 0; j < x; j++ {
+ w.Write(line[:j*2])
+ }
+ w.Flush()
+ }
+ })
+ }
+}
+
+func BenchmarkRagged(b *testing.B) {
+ var lines [8][]byte
+ for i, w := range [8]int{6, 2, 9, 5, 5, 7, 3, 8} {
+ // Build a line with x cells.
+ lines[i] = bytes.Repeat([]byte("a\t"), w)
+ lines[i] = append(lines[i], '\n')
+ }
+ for _, h := range [...]int{10, 100, 1000} {
+ b.Run(fmt.Sprintf("%d", h), func(b *testing.B) {
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ w := NewWriter(ioutil.Discard, 4, 4, 1, ' ', 0) // no particular reason for these settings
+ // Write the lines in turn h times.
+ for j := 0; j < h; j++ {
+ w.Write(lines[j%len(lines)])
+ }
+ w.Flush()
+ }
+ })
+ }
+}