github.com/cockroachdb/cockroachdb-parser@v0.23.3-0.20240213214944-911057d40c9a/pkg/util/timeutil/cpustopwatch.go (about) 1 // Copyright 2022 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package timeutil 12 13 import ( 14 "time" 15 16 "github.com/cockroachdb/cockroachdb-parser/pkg/util/grunning" 17 "github.com/cockroachdb/cockroachdb-parser/pkg/util/syncutil" 18 ) 19 20 // CPUStopWatch is a wrapper around cpuStopWatch that is safe to use 21 // concurrently. If CPUStopWatch is nil, all operations are no-ops and no 22 // locks are acquired. 23 type CPUStopWatch struct { 24 mu struct { 25 syncutil.Mutex 26 cpuStopWatch cpuStopWatch 27 } 28 } 29 30 // NewCPUStopWatch returns a new CPUStopWatch if the grunning library is 31 // supported. Otherwise, it returns nil. 32 func NewCPUStopWatch() *CPUStopWatch { 33 if grunning.Supported() { 34 return &CPUStopWatch{} 35 } 36 return nil 37 } 38 39 // Start starts the CPU stop watch if it hasn't already been started. 40 func (w *CPUStopWatch) Start() { 41 if w == nil { 42 return 43 } 44 w.mu.Lock() 45 defer w.mu.Unlock() 46 w.mu.cpuStopWatch.start() 47 } 48 49 // Stop stops the CPU stop watch if it hasn't already been stopped and 50 // accumulates the CPU time that has been spent since it was started. If the 51 // CPU stop watch has already been stopped, it is a noop. 52 func (w *CPUStopWatch) Stop() { 53 if w == nil { 54 return 55 } 56 w.mu.Lock() 57 defer w.mu.Unlock() 58 w.mu.cpuStopWatch.stop() 59 } 60 61 // Elapsed returns the total CPU time measured by the stop watch so far. 62 func (w *CPUStopWatch) Elapsed() time.Duration { 63 if w == nil { 64 return 0 65 } 66 w.mu.Lock() 67 defer w.mu.Unlock() 68 return w.mu.cpuStopWatch.elapsed() 69 } 70 71 // cpuStopWatch is a utility stop watch for measuring CPU time spent by a 72 // component. It can be safely started and stopped multiple times, but is 73 // not safe to use concurrently. If cpuStopWatch is nil, all operations are 74 // no-ops. 75 // 76 // Note that the grunning library uses a non-monotonic clock, so the measured 77 // duration between clock start and stop can come out as negative. This can lead 78 // to discrepancies in the measured CPU time - for example, a child stopwatch 79 // that is started and stopped while a parent stopwatch is running can rarely 80 // measure a larger CPU time duration than the parent. Users must be prepared to 81 // handle this case. 82 type cpuStopWatch struct { 83 startCPU time.Duration 84 totalCPU time.Duration 85 } 86 87 func (w *cpuStopWatch) start() { 88 if w == nil { 89 return 90 } 91 w.startCPU = grunning.Time() 92 } 93 94 func (w *cpuStopWatch) stop() { 95 if w == nil { 96 return 97 } 98 w.totalCPU += grunning.Elapsed(w.startCPU, grunning.Time()) 99 } 100 101 func (w *cpuStopWatch) elapsed() time.Duration { 102 if w == nil { 103 return 0 104 } 105 return w.totalCPU 106 }