github.com/loov/hrtime@v1.0.3/_example/benchtime/main.go (about)

     1  // benchtime implements measuring and plotting of timing overheads.
     2  package main
     3  
     4  import (
     5  	"flag"
     6  	"fmt"
     7  	"image/color"
     8  	"io/ioutil"
     9  	"runtime"
    10  	"runtime/debug"
    11  	"sort"
    12  	"time"
    13  	_ "unsafe"
    14  
    15  	"github.com/loov/hrtime"
    16  	"github.com/loov/plot"
    17  	"github.com/loov/plot/plotsvg"
    18  )
    19  
    20  var (
    21  	samples = flag.Int("samples", 1e6, "measurements per line")
    22  	warmup  = flag.Int("warmup", 256, "warmup count")
    23  	mintime = flag.Float64("min", 0, "minimum ns time to consider")
    24  	maxtime = flag.Float64("max", 100, "maximum ns time to consider")
    25  
    26  	svg    = flag.String("svg", "results.svg", "")
    27  	kernel = flag.Float64("kernel", 1, "kernel width")
    28  	width  = flag.Float64("width", 1500, "svg width")
    29  	height = flag.Float64("height", 150, "svg single-plot height")
    30  )
    31  
    32  func init() {
    33  	runtime.LockOSThread()
    34  }
    35  
    36  //go:linkname nanotime runtime.nanotime
    37  func nanotime() int64
    38  
    39  func main() {
    40  	flag.Parse()
    41  
    42  	N := *samples
    43  
    44  	times := make([]time.Time, N)
    45  	timenanos := make([]int64, N)
    46  	deltas := make([]time.Duration, N)
    47  	nanotimes := make([]int64, N)
    48  	qpcs := make([]int64, N)
    49  	rdtsc := make([]uint64, N)
    50  	rdtscp := make([]uint64, N)
    51  
    52  	var beforRDTSC, afterRDTSC, beforRDTSCP, afterRDTSCP time.Time
    53  
    54  	fmt.Println("benchmarking")
    55  	debug.SetGCPercent(-1)
    56  	{
    57  		fmt.Println("benchmarking time.Now")
    58  		runtime.GC()
    59  		for i := range times {
    60  			times[i] = time.Now()
    61  		}
    62  
    63  		fmt.Println("benchmarking time.UnixNano")
    64  		runtime.GC()
    65  		for i := range timenanos {
    66  			timenanos[i] = time.Now().UnixNano()
    67  		}
    68  
    69  		fmt.Println("benchmarking time.Since")
    70  		runtime.GC()
    71  		start := time.Now()
    72  		for i := range deltas {
    73  			deltas[i] = time.Since(start)
    74  		}
    75  
    76  		fmt.Println("benchmarking nanotime")
    77  		runtime.GC()
    78  		for i := range nanotimes {
    79  			nanotimes[i] = nanotime()
    80  		}
    81  
    82  		if runtime.GOOS == "windows" {
    83  			fmt.Println("benchmarking QPC")
    84  			runtime.GC()
    85  			for i := range qpcs {
    86  				qpcs[i] = QPC()
    87  			}
    88  		}
    89  
    90  		if hrtime.TSCSupported() {
    91  			fmt.Println("benchmarking RDTSC")
    92  			runtime.GC()
    93  
    94  			beforRDTSC = time.Now()
    95  			for i := range rdtsc {
    96  				rdtsc[i] = hrtime.RDTSC()
    97  			}
    98  			afterRDTSC = time.Now()
    99  
   100  			fmt.Println("benchmarking RDTSCP")
   101  			runtime.GC()
   102  			beforRDTSCP = time.Now()
   103  			for i := range rdtscp {
   104  				rdtscp[i] = hrtime.RDTSC()
   105  			}
   106  			afterRDTSCP = time.Now()
   107  
   108  			runtime.GC()
   109  		}
   110  	}
   111  	debug.SetGCPercent(100)
   112  
   113  	rdtscCalibration := float64(afterRDTSC.Sub(beforRDTSC).Nanoseconds()) / float64(rdtsc[N-1]-rdtsc[0])
   114  	rdtscpCalibration := float64(afterRDTSCP.Sub(beforRDTSCP).Nanoseconds()) / float64(rdtscp[N-1]-rdtscp[0])
   115  
   116  	offset := *warmup
   117  	ns_times := make([]float64, N-offset)
   118  	ns_timenanos := make([]float64, N-offset)
   119  	ns_deltas := make([]float64, N-offset)
   120  	ns_nanotimes := make([]float64, N-offset)
   121  	ns_qpcs := make([]float64, N-offset)
   122  	ns_rdtsc := make([]float64, N-offset)
   123  	ns_rdtscp := make([]float64, N-offset)
   124  
   125  	qpcmul := 1e9 / float64(QPCFrequency())
   126  	for i := range ns_times {
   127  		ns_times[i] = float64(times[offset+i].Sub(times[offset+i-1]).Nanoseconds())
   128  		ns_timenanos[i] = float64(timenanos[offset+i] - timenanos[offset+i-1])
   129  		ns_deltas[i] = float64((deltas[offset+i] - deltas[offset+i-1]).Nanoseconds())
   130  		ns_nanotimes[i] = float64(nanotimes[offset+i] - nanotimes[offset+i-1])
   131  		ns_qpcs[i] = float64(qpcs[offset+i]-qpcs[offset+i-1]) * qpcmul
   132  		ns_rdtsc[i] = float64(rdtsc[offset+i]-rdtsc[offset+i-1]) * rdtscCalibration
   133  		ns_rdtscp[i] = float64(rdtsc[offset+i]-rdtsc[offset+i-1]) * rdtscpCalibration
   134  	}
   135  
   136  	var timings = []*Timing{}
   137  	timings = append(timings,
   138  		&Timing{Name: "time.Now", Measured: ns_times},
   139  		&Timing{Name: "time.UnixNano", Measured: ns_timenanos},
   140  		&Timing{Name: "time.Since", Measured: ns_deltas},
   141  		&Timing{Name: "nanotime", Measured: ns_nanotimes},
   142  	)
   143  
   144  	if runtime.GOOS == "windows" {
   145  		timings = append(timings,
   146  			&Timing{Name: "QPC", Measured: ns_qpcs},
   147  		)
   148  	}
   149  
   150  	if hrtime.TSCSupported() {
   151  		timings = append(timings,
   152  			&Timing{Name: "RDTSC", Measured: ns_rdtsc},
   153  			&Timing{Name: "RDTSCP", Measured: ns_rdtscp},
   154  		)
   155  	}
   156  
   157  	p := plot.New()
   158  
   159  	p.X.Min = *mintime
   160  	p.X.Max = *maxtime
   161  	p.X.MajorTicks = 10
   162  	p.X.MinorTicks = 10
   163  
   164  	stack := plot.NewVStack()
   165  	stack.Margin = plot.R(5, 5, 5, 5)
   166  	p.Add(stack)
   167  
   168  	for _, timing := range timings {
   169  		timing.Prepare(*mintime, *maxtime)
   170  		density := plot.NewDensity("ns", timing.Sanitized)
   171  		density.Kernel = *kernel
   172  		density.Class = timing.Name
   173  		density.Stroke = color.NRGBA{0, 0, 0, 255}
   174  		density.Fill = color.NRGBA{0, 0, 0, 50}
   175  
   176  		flex := plot.NewHFlex()
   177  
   178  		text := plot.NewTextbox(
   179  			timing.Name,
   180  			fmt.Sprintf("Measured = %v", len(timing.Measured)),
   181  			fmt.Sprintf("Zeros = %v", timing.Zero),
   182  			fmt.Sprintf("Underlimit = %v", timing.Underlimit),
   183  			fmt.Sprintf("Overlimit = %v", timing.Overlimit),
   184  			fmt.Sprintf("99.9 = %v", int(timing.P999)),
   185  			fmt.Sprintf("99.99 = %v", int(timing.P9999)),
   186  			fmt.Sprintf("Max = %v", int(timing.Max)),
   187  		)
   188  		text.Style.Origin = plot.Point{X: -1, Y: 0}
   189  
   190  		flex.Add(130, text)
   191  		flex.AddGroup(0,
   192  			plot.NewGrid(),
   193  			density,
   194  			plot.NewTickLabels(),
   195  		)
   196  
   197  		stack.Add(flex)
   198  	}
   199  
   200  	svgcanvas := plotsvg.New(*width, *height*float64(len(timings)))
   201  	p.Draw(svgcanvas)
   202  
   203  	err := ioutil.WriteFile(*svg, svgcanvas.Bytes(), 0755)
   204  	if err != nil {
   205  		panic(err)
   206  	}
   207  }
   208  
   209  type Timing struct {
   210  	Name      string
   211  	Measured  []float64
   212  	Sanitized []float64
   213  
   214  	Zero       int
   215  	Underlimit int
   216  	Overlimit  int
   217  
   218  	P999  float64
   219  	P9999 float64
   220  	Max   float64
   221  }
   222  
   223  func (t *Timing) Prepare(min, max float64) {
   224  	t.Sanitized = append(t.Measured[:0:0], t.Measured...)
   225  	sort.Float64s(t.Sanitized)
   226  
   227  	t.P999 = t.Sanitized[(len(t.Sanitized)-1)*999/1000]
   228  	t.P9999 = t.Sanitized[(len(t.Sanitized)-1)*9999/10000]
   229  	t.Max = t.Sanitized[len(t.Sanitized)-1]
   230  
   231  	for i, v := range t.Sanitized {
   232  		if v <= 0 {
   233  			t.Zero++
   234  		} else if v <= min {
   235  			t.Sanitized[i] = min
   236  			t.Underlimit++
   237  		} else {
   238  			break
   239  		}
   240  	}
   241  
   242  	tail := len(t.Sanitized) - 1
   243  	for ; tail >= 0; tail-- {
   244  		if t.Sanitized[tail] < max {
   245  			break
   246  		}
   247  		t.Sanitized[tail] = max
   248  		t.Overlimit++
   249  	}
   250  
   251  	t.Sanitized = t.Sanitized[t.Zero:]
   252  }