]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: add deferreturn fast path for linked defers
authorMatthew Dempsky <mdempsky@google.com>
Thu, 3 Aug 2023 20:52:00 +0000 (13:52 -0700)
committerGopher Robot <gobot@golang.org>
Fri, 4 Aug 2023 16:37:20 +0000 (16:37 +0000)
A consequence of go.dev/cl/513837 was that calling deferreturn would
now use the unwinder to find (just) the current frame, and it turns
out there are workloads where this has a significant performance
impact.

As a simple optimization, this CL adds a fast path for deferreturn to
detect when there are pending linked defers, which allows us to skip
invoking the unwinder entirely.

Notably, this still doesn't handle the corner case of calling
deferreturn in a function that uses linked defer when dynamically
there just aren't any defers pending. It also means that after
recovering from a panic and returning to a frame that used open-coded,
we still need to use the unwinder too.

I hope to further optimize defer handling to improve these cases too,
but this is an easy, short-term optimization that relieves the
performance impact to the affected workloads.

Change-Id: I11fa73649302199eadccc27b403b231db8f33db2
Reviewed-on: https://go-review.googlesource.com/c/go/+/515716
Auto-Submit: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Keith Randall <khr@golang.org>
Reviewed-by: Keith Randall <khr@google.com>
src/runtime/panic.go

index d3aaa20cbceef7bfb815684db4e2a52e7736c67a..1ed40c71a41aa4b4bda868b40088748cf64e087c 100644 (file)
@@ -638,6 +638,14 @@ func (p *_panic) start(pc uintptr, sp unsafe.Pointer) {
        if !p.deferreturn {
                p.link = gp._panic
                gp._panic = (*_panic)(noescape(unsafe.Pointer(p)))
+       } else {
+               // Fast path for deferreturn: if there's a pending linked defer
+               // for this frame, then we know there aren't any open-coded
+               // defers, and we don't need to find the parent frame either.
+               if d := gp._defer; d != nil && d.sp == uintptr(sp) {
+                       p.sp = sp
+                       return
+               }
        }
 
        // Initialize state machine, and find the first frame with a defer.