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 }