github.com/jgbaldwinbrown/perf@v0.1.1/benchfmt/writer.go (about)

     1  // Copyright 2022 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package benchfmt
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"io"
    11  )
    12  
    13  // A Writer writes the Go benchmark format.
    14  type Writer struct {
    15  	w   io.Writer
    16  	buf bytes.Buffer
    17  
    18  	first      bool
    19  	fileConfig map[string]Config
    20  	order      []string
    21  }
    22  
    23  // NewWriter returns a writer that writes Go benchmark results to w.
    24  func NewWriter(w io.Writer) *Writer {
    25  	return &Writer{w: w, first: true, fileConfig: make(map[string]Config)}
    26  }
    27  
    28  // Write writes Record rec to w. If rec is a *Result and rec's file
    29  // configuration differs from the current file configuration in w, it
    30  // first emits the appropriate file configuration lines. For
    31  // Result.Values that have a non-zero OrigUnit, this uses OrigValue and
    32  // OrigUnit in order to better reproduce the original input.
    33  func (w *Writer) Write(rec Record) error {
    34  	switch rec := rec.(type) {
    35  	case *Result:
    36  		w.writeResult(rec)
    37  	case *UnitMetadata:
    38  		w.writeUnitMetadata(rec)
    39  	case *SyntaxError:
    40  		// Ignore
    41  		return nil
    42  	default:
    43  		return fmt.Errorf("unknown Record type %T", rec)
    44  	}
    45  
    46  	// Flush the buffer out to the io.Writer. Write to the buffer
    47  	// can't fail, so we only have to check if this fails.
    48  	_, err := w.w.Write(w.buf.Bytes())
    49  	w.buf.Reset()
    50  	return err
    51  }
    52  
    53  func (w *Writer) writeResult(res *Result) {
    54  	// If any file config changed, write out the changes.
    55  	if len(w.fileConfig) != len(res.Config) {
    56  		w.writeFileConfig(res)
    57  	} else {
    58  		for _, cfg := range res.Config {
    59  			if have, ok := w.fileConfig[cfg.Key]; !ok || !bytes.Equal(cfg.Value, have.Value) || cfg.File != have.File {
    60  				w.writeFileConfig(res)
    61  				break
    62  			}
    63  		}
    64  	}
    65  
    66  	// Print the benchmark line.
    67  	fmt.Fprintf(&w.buf, "Benchmark%s %d", res.Name, res.Iters)
    68  	for _, val := range res.Values {
    69  		if val.OrigUnit == "" {
    70  			fmt.Fprintf(&w.buf, " %v %s", val.Value, val.Unit)
    71  		} else {
    72  			fmt.Fprintf(&w.buf, " %v %s", val.OrigValue, val.OrigUnit)
    73  		}
    74  	}
    75  	w.buf.WriteByte('\n')
    76  
    77  	w.first = false
    78  }
    79  
    80  func (w *Writer) writeFileConfig(res *Result) {
    81  	if !w.first {
    82  		// Configuration blocks after results get an extra blank.
    83  		w.buf.WriteByte('\n')
    84  		w.first = true
    85  	}
    86  
    87  	// Walk keys we know to find changes and deletions.
    88  	for i := 0; i < len(w.order); i++ {
    89  		key := w.order[i]
    90  		have := w.fileConfig[key]
    91  		idx, ok := res.ConfigIndex(key)
    92  		if !ok {
    93  			// Key was deleted.
    94  			fmt.Fprintf(&w.buf, "%s:\n", key)
    95  			delete(w.fileConfig, key)
    96  			copy(w.order[i:], w.order[i+1:])
    97  			w.order = w.order[:len(w.order)-1]
    98  			i--
    99  			continue
   100  		}
   101  		cfg := &res.Config[idx]
   102  		if bytes.Equal(have.Value, cfg.Value) && have.File == cfg.File {
   103  			// Value did not change.
   104  			continue
   105  		}
   106  		// Value changed.
   107  		if cfg.File {
   108  			// Omit internal config.
   109  			fmt.Fprintf(&w.buf, "%s: %s\n", key, cfg.Value)
   110  		}
   111  		have.Value = append(have.Value[:0], cfg.Value...)
   112  		have.File = cfg.File
   113  		w.fileConfig[key] = have
   114  	}
   115  
   116  	// Find new keys.
   117  	if len(w.fileConfig) != len(res.Config) {
   118  		for _, cfg := range res.Config {
   119  			if _, ok := w.fileConfig[cfg.Key]; ok {
   120  				continue
   121  			}
   122  			// New key.
   123  			if cfg.File {
   124  				fmt.Fprintf(&w.buf, "%s: %s\n", cfg.Key, cfg.Value)
   125  			}
   126  			w.fileConfig[cfg.Key] = Config{cfg.Key, append([]byte(nil), cfg.Value...), cfg.File}
   127  			w.order = append(w.order, cfg.Key)
   128  		}
   129  	}
   130  
   131  	w.buf.WriteByte('\n')
   132  }
   133  
   134  func (w *Writer) writeUnitMetadata(m *UnitMetadata) {
   135  	fmt.Fprintf(&w.buf, "Unit %s %s=%s\n", m.OrigUnit, m.Key, m.Value)
   136  }