github.com/loov/hrtime@v1.0.3/tsc.go (about) 1 package hrtime 2 3 import ( 4 "sync" 5 "time" 6 ) 7 8 // Count represents represents Time Stamp Counter value, when available. 9 // 10 // Count doesn't depend on power throttling making it useful for benchmarking. 11 // However it is not reliably convertible to a reasonable time-value. 12 type Count int64 13 14 var calibrateOnce sync.Once 15 16 // ApproxDuration returns approximate conversion into a Duration. 17 // 18 // First call to this function will do calibration and can take several milliseconds. 19 func (count Count) ApproxDuration() time.Duration { 20 calibrateOnce.Do(calculateTSCConversion) 21 return time.Duration(count) * ratioNano / time.Duration(ratioCount) 22 } 23 24 // TSC reads the current Time Stamp Counter value. 25 // 26 // Reminder: Time Stamp Count are processor specific and need to be converted to 27 // time.Duration with Count.ApproxDuration. 28 func TSC() Count { return Count(RDTSC()) } 29 30 // TSCSince returns count since start. 31 // 32 // Reminder: Count is processor specific and need to be converted to 33 // time.Duration with Count.ApproxDuration. 34 func TSCSince(start Count) Count { return TSC() - start } 35 36 // TSCSupported returns whether processor supports giving invariant time stamp counter values 37 func TSCSupported() bool { return rdtscpInvariant } 38 39 // TSCOverhead returns overhead of Count call 40 func TSCOverhead() Count { return readTSCOverhead } 41 42 var ( 43 rdtscpInvariant = false 44 readTSCOverhead Count 45 46 cpuid func(op1, op2 uint32) (eax, ebx, ecx, edx uint32) 47 48 ratioNano time.Duration 49 ratioCount Count 50 ) 51 52 func calculateTSCOverhead() { 53 if !rdtscpInvariant { 54 return 55 } 56 57 start := TSC() 58 for i := 0; i < calibrationCalls; i++ { 59 TSC() 60 } 61 stop := TSC() 62 63 readTSCOverhead = (stop - start) / (calibrationCalls + 1) 64 } 65 66 func calculateTSCConversion() { 67 // warmup 68 for i := 0; i < 64*calibrationCalls; i++ { 69 empty() 70 } 71 72 nanostart := Now() 73 countstart := TSC() 74 for i := 0; i < 64*calibrationCalls; i++ { 75 empty() 76 } 77 nanoend := Now() 78 countstop := TSC() 79 80 // TODO: figure out a better way to calculate this 81 ratioNano = nanoend - nanostart - Overhead() 82 ratioCount = countstop - countstart - TSCOverhead() 83 } 84 85 //go:noinline 86 func empty() {}