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

Lazily render mock diff output on successful match #1615

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

mikeauclair
Copy link

Summary

  • For mocks on functions that take complex types as arg, Sprintf-ing the actual value can be costly. Switch successful matches to use thunks to lazily render their output, and fully avoid rendering their output when there are no differences since we return a constant string in that case anyways

Changes

  • Switch to a string builder for accumulating output for better performance even when we do render

Motivation

  • We heavily use mocks at DevotedHealth, and use them frequently in gRPC contexts where we're mocking RPCs that take <Whatever>Request structs that are costly to %v via Sprintf. Sprintf showed up as a tangible contributor to runtime performance when profiling some of our heavier tests. The below ~5s cpu time spent on Sprintf goes away completely here in one of our exemplar tests
Screenshot 2024-06-25 at 12 16 35 PM

Related issues

@dolmen dolmen added pkg-mock Any issues related to Mock performance labels Oct 4, 2024
@brackendawson
Copy link
Collaborator

This includes the CallerInfo change from #1614

Copy link
Collaborator

@brackendawson brackendawson left a comment

Choose a reason for hiding this comment

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

I've considered and tried a similar approach before when trying to address #1597. It only removes the race from passing tests, though. If it comes with a sizable performance benefit I'm more than happy to lazily render these.

I'd like it to be separated from #1614, they're not dependant on one another, right?

Then there's some changes I'd like around the use of outputRenderers. I'm fairly sure the slice isn't even necessary, you can change actualFmt's type from string to func() string and change all its references to actualFmt().

Comment on lines 276 to 290
if !more {
// We know we already have less than a buffer's worth of frames
if !maybeMore {
break
}
offset += stackFrameBufferSize
n = runtime.Callers(offset, pcs)
if n == 0 {
break
}

maybeMore = n == stackFrameBufferSize

frames = runtime.CallersFrames(pcs[:n])
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm not a fan of more and maybeMore, this code is actually two nested loops and I think it would be more readable if you coded it that way. I think that pure behaviour is usually better than data (varibles) used only to alter behaviour.

This code is also from #1614, it might be quicker to review this change if you keep the two PRs separate.

Copy link
Author

Choose a reason for hiding this comment

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

I split this back out. My suspicion is that I opened PRs both on upstream and on our fork, and in the process of merging this into master on our fork the branch got updated.

var differences int

maxArgCount := len(args)
if len(objects) > maxArgCount {
maxArgCount = len(objects)
}

for i := 0; i < maxArgCount; i++ {
outputRenderers := []outputRenderer{}
Copy link
Collaborator

Choose a reason for hiding this comment

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

interface{} says nothing, the case for this being type string isn't actually necessary, you can just use a static function. Then you no longer need the unreachable panic.

Suggested change
outputRenderers := []outputRenderer{}
outputRenderers := []func() string

Copy link
Author

@mikeauclair mikeauclair Dec 17, 2024

Choose a reason for hiding this comment

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

Yeah I waffled back and forth on that for a bit. Can definitely simplify every renderer down to func() string

@mikeauclair
Copy link
Author

I'd like it to be separated from #1614, they're not dependant on one another, right?

I honestly don't remember if they are or are not given how long it's been since I wrote this. I can try to take a pass on cleaning this and the other PR up after next week (short week for thanksgiving in the states)

@mikeauclair mikeauclair force-pushed the mauclair-mock-match-sprintf branch from d45244f to 3d98e69 Compare December 17, 2024 18:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
performance pkg-mock Any issues related to Mock
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants