aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/tracecpu.go
diff options
context:
space:
mode:
authorMichael Anthony Knyszek <mknyszek@google.com>2025-11-19 03:17:54 +0000
committerMichael Knyszek <mknyszek@google.com>2025-11-21 14:04:31 -0800
commitd68aec8db1bc3c167d2f0e5fdee8c1346ee35418 (patch)
tree3faf1812974764db8e7c1941f8651f9faddb1470 /src/runtime/tracecpu.go
parent8d9906cd34a1052868c1c0273e6f2d22632e0e84 (diff)
downloadgo-d68aec8db1bc3c167d2f0e5fdee8c1346ee35418.tar.xz
runtime: replace trace seqlock with write flag
The runtime tracer currently uses a per-M seqlock to indicate whether a thread is writing to a local trace buffer. The seqlock is updated with two atomic adds, read-modify-write operations. These are quite expensive, even though they're completely uncontended. We can make these operations slightly cheaper by using an atomic store. The key insight here is that only one thread ever writes to the value at a time, so only the "write" of the read-modify-write actually matters. At that point, it doesn't really matter that we have a monotonically increasing counter. This is made clearer by the fact that nothing other than basic checks make sure the counter is monotonically increasing: everything only depends on whether the counter is even or odd. At that point, all we really need is a flag: an atomic.Bool, which we can update with an atomic Store, a write-only instruction. Change-Id: I0cfe39b34c7634554c34c53c0f0e196d125bbc4a Reviewed-on: https://go-review.googlesource.com/c/go/+/721840 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Michael Pratt <mpratt@google.com>
Diffstat (limited to 'src/runtime/tracecpu.go')
-rw-r--r--src/runtime/tracecpu.go14
1 files changed, 7 insertions, 7 deletions
diff --git a/src/runtime/tracecpu.go b/src/runtime/tracecpu.go
index e64ca32cdf..c9c3a1511f 100644
--- a/src/runtime/tracecpu.go
+++ b/src/runtime/tracecpu.go
@@ -224,20 +224,20 @@ func traceCPUSample(gp *g, mp *m, pp *p, stk []uintptr) {
// We're going to conditionally write to one of two buffers based on the
// generation. To make sure we write to the correct one, we need to make
- // sure this thread's trace seqlock is held. If it already is, then we're
+ // sure this thread's trace write flag is set. If it already is, then we're
// in the tracer and we can just take advantage of that. If it isn't, then
// we need to acquire it and read the generation.
locked := false
- if mp.trace.seqlock.Load()%2 == 0 {
- mp.trace.seqlock.Add(1)
+ if !mp.trace.writing.Load() {
+ mp.trace.writing.Store(true)
locked = true
}
gen := trace.gen.Load()
if gen == 0 {
- // Tracing is disabled, as it turns out. Release the seqlock if necessary
+ // Tracing is disabled, as it turns out. Clear the write flag if necessary
// and exit.
if locked {
- mp.trace.seqlock.Add(1)
+ mp.trace.writing.Store(false)
}
return
}
@@ -275,8 +275,8 @@ func traceCPUSample(gp *g, mp *m, pp *p, stk []uintptr) {
trace.signalLock.Store(0)
- // Release the seqlock if we acquired it earlier.
+ // Clear the write flag if we set it earlier.
if locked {
- mp.trace.seqlock.Add(1)
+ mp.trace.writing.Store(false)
}
}