1
0
mirror of https://github.com/golang/go synced 2024-11-25 10:17:57 -07:00

runtime: don't acquirem() in vgetrandom unless necessary

I noticed in pprof that acquirem() was a bit of a hotspot. It turns out
that we can use the same trick that runtime.rand() does, and only
acquirem if we're doing something non-nosplit -- in this case, getting a
new state -- but otherwise just do getg().m, which is safe because we're
inside runtime and don't call split functions.

cpu: 11th Gen Intel(R) Core(TM) i7-11850H @ 2.50GHz
                     │   sec/op    │   sec/op     vs base               │
ParallelGetRandom-16   2.651n ± 4%   2.416n ± 7%  -8.87% (p=0.001 n=10)
                     │     B/s      │     B/s       vs base               │
ParallelGetRandom-16   1.406Gi ± 4%   1.542Gi ± 6%  +9.72% (p=0.001 n=10)

Change-Id: Iae075f4e298b923e499cd01adfabacab725a8684
Reviewed-on: https://go-review.googlesource.com/c/go/+/616738
Reviewed-by: Michael Pratt <mpratt@google.com>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
Jason A. Donenfeld 2024-10-01 22:19:23 +02:00 committed by Jason Donenfeld
parent dc8902f4eb
commit 8c269479ed

View File

@ -87,19 +87,26 @@ func vgetrandom(p []byte, flags uint32) (ret int, supported bool) {
return -1, false
}
mp := acquirem()
// We use getg().m instead of acquirem() here, because always taking
// the lock is slightly more expensive than not always taking the lock.
// However, we *do* require that m doesn't migrate elsewhere during the
// execution of the vDSO. So, we exploit two details:
// 1) Asynchronous preemption is aborted when PC is in the runtime.
// 2) Most of the time, this function only calls vgetrandom1(), which
// does not have a preamble that synchronously preempts.
// We do need to take the lock when getting a new state for m, but this
// is very much the slow path, in the sense that it only ever happens
// once over the entire lifetime of an m. So, a simple getg().m suffices.
mp := getg().m
if mp.vgetrandomState == 0 {
mp.locks++
state := vgetrandomGetState()
mp.locks--
if state == 0 {
releasem(mp)
return -1, false
}
mp.vgetrandomState = state
}
ret = vgetrandom1(unsafe.SliceData(p), uintptr(len(p)), flags, mp.vgetrandomState, vgetrandomAlloc.stateSize)
supported = true
releasem(mp)
return
return vgetrandom1(unsafe.SliceData(p), uintptr(len(p)), flags, mp.vgetrandomState, vgetrandomAlloc.stateSize), true
}