From fff4f599fe1c21e411a99de5c9b3777d06ce0ce6 Mon Sep 17 00:00:00 2001 From: Keith Randall Date: Thu, 11 Apr 2019 09:50:59 -0700 Subject: cmd/compile,runtime: allocate defer records on the stack MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a defer is executed at most once in a function body, we can allocate the defer record for it on the stack instead of on the heap. This should make defers like this (which are very common) faster. This optimization applies to 363 out of the 370 static defer sites in the cmd/go binary. name old time/op new time/op delta Defer-4 52.2ns ± 5% 36.2ns ± 3% -30.70% (p=0.000 n=10+10) Fixes #6980 Update #14939 Change-Id: I697109dd7aeef9e97a9eeba2ef65ff53d3ee1004 Reviewed-on: https://go-review.googlesource.com/c/go/+/171758 Run-TryBot: Keith Randall TryBot-Result: Gobot Gobot Reviewed-by: Austin Clements --- src/runtime/stack_test.go | 55 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) (limited to 'src/runtime/stack_test.go') diff --git a/src/runtime/stack_test.go b/src/runtime/stack_test.go index df73b3a1d5..143d3a99a0 100644 --- a/src/runtime/stack_test.go +++ b/src/runtime/stack_test.go @@ -799,3 +799,58 @@ func TestDeferLiveness(t *testing.T) { t.Errorf("output:\n%s\n\nwant no output", output) } } + +func TestDeferHeapAndStack(t *testing.T) { + P := 4 // processors + N := 10000 //iterations + D := 200 // stack depth + + if testing.Short() { + P /= 2 + N /= 10 + D /= 10 + } + c := make(chan bool) + for p := 0; p < P; p++ { + go func() { + for i := 0; i < N; i++ { + if deferHeapAndStack(D) != 2*D { + panic("bad result") + } + } + c <- true + }() + } + for p := 0; p < P; p++ { + <-c + } +} + +// deferHeapAndStack(n) computes 2*n +func deferHeapAndStack(n int) (r int) { + if n == 0 { + return 0 + } + if n%2 == 0 { + // heap-allocated defers + for i := 0; i < 2; i++ { + defer func() { + r++ + }() + } + } else { + // stack-allocated defers + defer func() { + r++ + }() + defer func() { + r++ + }() + } + r = deferHeapAndStack(n - 1) + escapeMe(new([1024]byte)) // force some GCs + return +} + +// Pass a value to escapeMe to force it to escape. +var escapeMe = func(x interface{}) {} -- cgit v1.3-5-g9baa