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() {}