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

Add TraceFrom(pcs) for "on-demand" StackTrace instantiation #25

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from

Conversation

lukseven
Copy link

Allows to record just the pcs from runtime.Callers when an error
occurs and convert them to a StackTrace only if the trace is actually
needed, e.g. when printing to a log.

Improves performance about 4x.

Allows to record just the pcs from runtime.Callers when an error
occurs and convert them to a StackTrace only if the trace is actually
needed, e.g. when printing to a log.

Improves performance about 4x.
@coveralls
Copy link

Coverage Status

Coverage increased (+0.2%) to 92.237% when pulling 301ad14 on eluv-io:add-trace-from into 2fee6af on go-stack:master.

@ChrisHines
Copy link
Member

This is an interesting idea. I would like to think about this a bit before deciding to merge it. In the mean time, would you kindly target this PR to the develop branch instead? This project is using a git-flow branching model, so that master only gets new commits when releases are tagged. Thanks.

@lukseven lukseven changed the base branch from master to develop February 1, 2021 09:55
@lukseven
Copy link
Author

lukseven commented Feb 1, 2021

Re-targeted to develop.

@ChrisHines
Copy link
Member

Thanks for retargeting. I've taken a closer look at this PR and run the benchmarks. I'm persuaded that this would be a valuable addition, but I see some things we should improve before merging. I'll try post full review comments before the end of the week.

Copy link
Member

@ChrisHines ChrisHines left a comment

Choose a reason for hiding this comment

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

Thanks for the PR. Please make the changes I've suggested below.

@@ -208,14 +208,24 @@ func (cs CallStack) Format(s fmt.State, verb rune) {
s.Write(closeBracketBytes)
}

// Convenience wrapper around runtime.Callers()
func Callers(skip int) []uintptr {
Copy link
Member

Choose a reason for hiding this comment

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

I think we should choose a function name that aligns more closely to the Trace function since they both serve the same purpose just at different levels of abstraction. We should also improve the doc comment while we're at it.

Consider:

// TraceFast returns a low level representation of a CallStack. Use TraceFrom
// to convert the result into a CallStack. Use TraceFast as a lighter alternative
// to Trace when you want to record stack trace but will not always format it.
func TraceFast() []uintptr { ... }

cs := make(CallStack, 0, n)
// TraceFrom creates a CallStack from the given program counters (as generated
// by runtime.Callers)
func TraceFrom(pcs []uintptr) CallStack {
Copy link
Member

Choose a reason for hiding this comment

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

The docs for TraceFrom should make it clear that it should only be given a return value from TraceFast and not just any slice of pcs. That gives us some wiggle room to adjust the implemenation without having to be compatible with arbitrary pcs []uintptr values.

}

func BenchmarkCallers50(b *testing.B) {
b.StopTimer()
Copy link
Member

Choose a reason for hiding this comment

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

We should call b.StopTimer inside the loop before calling deepCallers like it is in BenchmarkCallers10. I realize you followed the pattern I had in the BenchmarkTrace functions but I did it wrong there and never noticed until now. Please fix those too if you don't mind.

@ChrisHines
Copy link
Member

@lukseven We may be able to get the performance gains you want without adding new functions to the API. This package used to be more efficient prior to the introduction of runtime.CallersFrames and adoption of that API. See the v1.7.0 release notes for some links with more details. But I have been reviewing the commit history for the implementation of runtime.CallersFrames and discovered that it was dramatically refactored in Go 1.12 in a way that I think makes it possible for this package to return to a lazy evaluation model without the issues introduced by Go 1.9 that we ran into before.

It would be great if we can get the lower upfront cost you want in the existing stack.Trace function and avoid adding two new functions that would clutter and confuse the package API.

Let me know if you want to make an attempt at that.

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