Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement one shot SHA and reduce allocs #24

Merged
merged 6 commits into from
May 17, 2022
Merged

Implement one shot SHA and reduce allocs #24

merged 6 commits into from
May 17, 2022

Conversation

qmuntal
Copy link
Member

@qmuntal qmuntal commented May 12, 2022

This PR contains two performance improvements which landed into dev.boringbranch during this development cycle, both committed in https://go-review.googlesource.com/c/go/+/395876/11.

  • a99ec67: Implement one shot SHA calls. This is helpful for callers which don't need to keep the hash object returned by NewSha1 and friends, as these functions return a pointer which normally escapes in the caller side.
  • 9fafecd and 31857f3: Makes SHA calls allocation-free by hiding openssl pointers from the escape analysis.

The benchmarks demonstrates that the new code does not allocate:

name          old time/op    new time/op    delta
Hash8Bytes-4    4.57µs ± 4%    4.46µs ± 4%    -2.55%  (p=0.009 n=10+10)

name          old speed      new speed      delta
Hash8Bytes-4   224MB/s ± 4%   230MB/s ± 4%    +2.62%  (p=0.009 n=10+10)

name          old bytes/op   new bytes/op   delta
Hash8Bytes-4     24.0B ± 0%      0.0B       -100.00%  (p=0.000 n=10+10)

name          old allocs/op  new allocs/op  delta
Hash8Bytes-4      1.00 ± 0%      0.00       -100.00%  (p=0.000 n=10+10)

Removing the SHA allocations is important for when openssl is integrated into go1.19, else we would have to disable a new test that checks boring SHAs don't allocate.

@qmuntal qmuntal requested review from jaredpar, dagood and chsienki May 12, 2022 12:12
// compiles down to zero instructions.
// USE CAREFULLY!
//go:nosplit
func noescape(p unsafe.Pointer) unsafe.Pointer {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you help me understand what impact this has on the program? I believe the intent is to trick the Go compiler into thinking a pointer cannot escape when it reality it does. What benefit does that end up having though?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I read up a bit about this. Escape logic impacts cgo in the sense that it's important that pointers which are passed to C and are stored there need to be allocated on the heap. If they are allocated on the stack then that creates a memory hole. The default of cgo is to ensure all pointers are marked as escaping so they get pushed to the heap (based on the Russ change). This is reasonable because it's the safest default.

The noescape function is then used to trick the cgo tooling. Basically and "I know better, this isn't stored" escape hatch for developers

This is my reading of it at least

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have the same sense based on golang/go#10303.

golang/go#24450 also points out that even though cgo has some strict-seeming pointer passing prerequisites, it's still not always easy to identify when it's safe to noescape. (If the C function calls back into Go, Go might rearrange the stack, so even having a pointer to something in the Go stack in a C parameter or local variable can turn out badly.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To the best of my knowledge, OpenSSL is not storing the few pointers we are passing as noescape, so it is safe to trick cgo tooling.

@qmuntal
Copy link
Member Author

qmuntal commented May 12, 2022

I've found something good: noescapeCtx is not necessary because we are passing OpenSSL C pointers as uintptr instead of unsafe.Pointer, and the former are already safely hidden from the escape analysis.

So there are only three remaining usages of noescape, but we can't avoid them because they work on Go pointers.

Copy link
Member

@dagood dagood left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, a few questions/suggestions.

openssl/sha_test.go Outdated Show resolved Hide resolved
openssl/sha_test.go Outdated Show resolved Hide resolved
openssl/sha_test.go Outdated Show resolved Hide resolved
openssl/goopenssl.h Show resolved Hide resolved
openssl/sha.go Outdated Show resolved Hide resolved
@qmuntal qmuntal merged commit de376e2 into main May 17, 2022
@qmuntal qmuntal deleted the dev/qmuntal/perf branch May 17, 2022 19:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants