aboutsummaryrefslogtreecommitdiff
path: root/src/sync
diff options
context:
space:
mode:
authorCarlo Alberto Ferraris <cafxx@strayorange.com>2018-11-13 15:34:22 +0900
committerBrad Fitzpatrick <bradfitz@golang.org>2019-03-09 05:58:31 +0000
commitca8354843ef9f30207efd0a40bb6c53e7ba86892 (patch)
treea0be727b5e19672923b7f063d5db9c6126e6c6e8 /src/sync
parent41cb0aedffdf4c5087de82710c4d016a3634b4ac (diff)
downloadgo-ca8354843ef9f30207efd0a40bb6c53e7ba86892.tar.gz
go-ca8354843ef9f30207efd0a40bb6c53e7ba86892.zip
sync: allow inlining the Once.Do fast path
Using Once.Do is now extremely cheap because the fast path is just an inlined atomic load of a variable that is written only once and a conditional jump. This is very beneficial for Once.Do because, due to its nature, the fast path will be used for every call after the first one. In a attempt to mimize code size increase, reorder the fields so that the pointer to Once is also the pointer to Once.done, that is the only field used in the hot path. This allows to use more compact instruction encodings or less instructions in the hot path (that is inlined at every callsite). name old time/op new time/op delta Once 4.54ns ± 0% 2.06ns ± 0% -54.59% (p=0.000 n=19+16) Once-4 1.18ns ± 0% 0.55ns ± 0% -53.39% (p=0.000 n=15+16) Once-16 0.53ns ± 0% 0.17ns ± 0% -67.92% (p=0.000 n=18+17) linux/amd64 bin/go 14675861 (previous commit 14663387, +12474/+0.09%) Change-Id: Ie2708103ab473787875d66746d2f20f1d90a6916 Reviewed-on: https://go-review.googlesource.com/c/go/+/152697 Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org> Reviewed-by: Austin Clements <austin@google.com>
Diffstat (limited to 'src/sync')
-rw-r--r--src/sync/once.go16
1 files changed, 12 insertions, 4 deletions
diff --git a/src/sync/once.go b/src/sync/once.go
index d8ef952ea5..84761970dd 100644
--- a/src/sync/once.go
+++ b/src/sync/once.go
@@ -10,8 +10,13 @@ import (
// Once is an object that will perform exactly one action.
type Once struct {
- m Mutex
+ // done indicates whether the action has been performed.
+ // It is first in the struct because it is used in the hot path.
+ // The hot path is inlined at every call site.
+ // Placing done first allows more compact instructions on some architectures (amd64/x86),
+ // and fewer instructions (to calculate offset) on other architectures.
done uint32
+ m Mutex
}
// Do calls the function f if and only if Do is being called for the
@@ -33,10 +38,13 @@ type Once struct {
// without calling f.
//
func (o *Once) Do(f func()) {
- if atomic.LoadUint32(&o.done) == 1 {
- return
+ if atomic.LoadUint32(&o.done) == 0 {
+ // Outlined slow-path to allow inlining of the fast-path.
+ o.doSlow(f)
}
- // Slow-path.
+}
+
+func (o *Once) doSlow(f func()) {
o.m.Lock()
defer o.m.Unlock()
if o.done == 0 {