| // Copyright 2017 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package benchstat |
| |
| import ( |
| "fmt" |
| "strings" |
| ) |
| |
| // A Scaler is a function that scales and formats a measurement. |
| // All measurements within a given table row are formatted |
| // using the same scaler, so that the units are consistent |
| // across the row. |
| type Scaler func(float64) string |
| |
| // NewScaler returns a Scaler appropriate for formatting |
| // the measurement val, which has the given unit. |
| func NewScaler(val float64, unit string) Scaler { |
| if hasBaseUnit(unit, "ns/op") || hasBaseUnit(unit, "ns/GC") { |
| return timeScaler(val) |
| } |
| |
| var format string |
| var scale float64 |
| var suffix string |
| |
| prescale := 1.0 |
| if hasBaseUnit(unit, "MB/s") { |
| prescale = 1e6 |
| } |
| |
| switch x := val * prescale; { |
| case x >= 99500000000000: |
| format, scale, suffix = "%.0f", 1e12, "T" |
| case x >= 9950000000000: |
| format, scale, suffix = "%.1f", 1e12, "T" |
| case x >= 995000000000: |
| format, scale, suffix = "%.2f", 1e12, "T" |
| case x >= 99500000000: |
| format, scale, suffix = "%.0f", 1e9, "G" |
| case x >= 9950000000: |
| format, scale, suffix = "%.1f", 1e9, "G" |
| case x >= 995000000: |
| format, scale, suffix = "%.2f", 1e9, "G" |
| case x >= 99500000: |
| format, scale, suffix = "%.0f", 1e6, "M" |
| case x >= 9950000: |
| format, scale, suffix = "%.1f", 1e6, "M" |
| case x >= 995000: |
| format, scale, suffix = "%.2f", 1e6, "M" |
| case x >= 99500: |
| format, scale, suffix = "%.0f", 1e3, "k" |
| case x >= 9950: |
| format, scale, suffix = "%.1f", 1e3, "k" |
| case x >= 995: |
| format, scale, suffix = "%.2f", 1e3, "k" |
| case x >= 99.5: |
| format, scale, suffix = "%.0f", 1, "" |
| case x >= 9.95: |
| format, scale, suffix = "%.1f", 1, "" |
| default: |
| format, scale, suffix = "%.2f", 1, "" |
| } |
| |
| if hasBaseUnit(unit, "B/op") || hasBaseUnit(unit, "bytes/op") || hasBaseUnit(unit, "bytes") { |
| suffix += "B" |
| } |
| if hasBaseUnit(unit, "MB/s") { |
| suffix += "B/s" |
| } |
| scale /= prescale |
| |
| return func(val float64) string { |
| return fmt.Sprintf(format+suffix, val/scale) |
| } |
| } |
| |
| func timeScaler(ns float64) Scaler { |
| var format string |
| var scale float64 |
| switch x := ns / 1e9; { |
| case x >= 99.5: |
| format, scale = "%.0fs", 1 |
| case x >= 9.95: |
| format, scale = "%.1fs", 1 |
| case x >= 0.995: |
| format, scale = "%.2fs", 1 |
| case x >= 0.0995: |
| format, scale = "%.0fms", 1000 |
| case x >= 0.00995: |
| format, scale = "%.1fms", 1000 |
| case x >= 0.000995: |
| format, scale = "%.2fms", 1000 |
| case x >= 0.0000995: |
| format, scale = "%.0fµs", 1000*1000 |
| case x >= 0.00000995: |
| format, scale = "%.1fµs", 1000*1000 |
| case x >= 0.000000995: |
| format, scale = "%.2fµs", 1000*1000 |
| case x >= 0.0000000995: |
| format, scale = "%.0fns", 1000*1000*1000 |
| case x >= 0.00000000995: |
| format, scale = "%.1fns", 1000*1000*1000 |
| default: |
| format, scale = "%.2fns", 1000*1000*1000 |
| } |
| return func(ns float64) string { |
| return fmt.Sprintf(format, ns/1e9*scale) |
| } |
| } |
| |
| // hasBaseUnit reports whether s has unit unit. |
| // For now, it reports whether s == unit or s ends in -unit. |
| func hasBaseUnit(s, unit string) bool { |
| return s == unit || strings.HasSuffix(s, "-"+unit) |
| } |