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

Reuse frames in profile #1570

Merged
merged 5 commits into from
Jul 31, 2023
Merged

Reuse frames in profile #1570

merged 5 commits into from
Jul 31, 2023

Conversation

stayallive
Copy link
Collaborator

@stayallive stayallive commented Jul 14, 2023

Currently we just insert every frame in the profile, however we can lower the payload size by not doing that and reusing identical frames (which in most applications are the first few frames of every stack) and also reuse identical stacks. This results in a significant smaller payload. This saves on data we need to transmit saving on ms spent transmatting to Sentry and saving Sentry on storage.

From some googling for benchmarks I found that md5(serialize($frame)) would be a good tradeoff between correctness (collisions) and speed.

As a very non-scientific test I deployed this to a production app and looked at similar length (time) and same amount of sample profiles of the same transaction and saw a reduction from 145 KB to 44 KB when downloading the profile JSON from the Sentry UI which I thougt was pretty neat 😄

@Jean85
Copy link
Collaborator

Jean85 commented Jul 14, 2023

Is there a measurable CPU overhead with this approach?

But I would imagine that the saving in data sending would cover that...

@stayallive
Copy link
Collaborator Author

There is an overhead (obviously) when doing 100k runs of one of the tests the wall time on my system goes from 0.45s to 0.95s. For a single runs it is not measurable (with my tools).

I did not try what the impact is on serializing it as JSON and compressing to send of to Sentry, but I assume that it is faster with less data but no idea if true and or a significant number.

I also tried this instead as a hash:

$frameHash = "f{$frame['filename']};a{$frame['abs_path']};m{$frame['module']};fn{$frame['function']};l{$frame['lineno']}";

// and just:

$frameHash = serialize($frame);

This is faster (0.65s and 0.69s respectively) but not sure if it's worth it since this first one is easy to mess up and I'm also not sure how efficient having very large array keys is.

I'm open to better suggestions if you have them (both for better de-dup algo and a way to test this), but from what I can gather this seems the most efficient and correct way to ensure unique values (if the values are arrays) in PHP.

Copy link
Member

@armcknight armcknight left a comment

Choose a reason for hiding this comment

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

@stayallive Thanks for submitting this PR. Looks like the right track. For reference this is how we implemented it in the Cocoa SDK: https://github.com/getsentry/sentry-cocoa/blob/a176fc448cf5f3ea7b188449b8170b14ce6805cf/Sources/Sentry/include/SentryProfilerState.h#L37-L74 (implementation here).

We also similarly do a stack dedupe, as if a leaf function takes more than 10ms the stack will be identical across samples; would you be willing to add that as well?

@stayallive
Copy link
Collaborator Author

That makes a lot of sense, thanks for linking to another implementation, didn’t even look for one… will take a look at the stack dedupe too, makes sense!

Copy link

@phacops phacops left a comment

Choose a reason for hiding this comment

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

Looks good to me. Big plus if you de-duplicate stacks as well.

src/Profiling/Profile.php Outdated Show resolved Hide resolved
@stayallive
Copy link
Collaborator Author

stayallive commented Jul 26, 2023

Thanks @phacops and @armcknight for the suggestions, implemented stack re-using too (same method as for frames).

$function = $frame['function'];
} else {
// /index.php
$function = $file;
}

$frames[] = [
$stackFrames[] = $registerFrame([
Copy link
Member

Choose a reason for hiding this comment

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

Not sure if this is applicable for PHP but in python, since we're able to use a triple composed of

  • abs_path
  • lineno
  • function
    as the map key. Normally abs_path and lineno suffice already but python allows you to patch over functions so we added function to dedupe that case.

Because of this, one thing we were able to do in python is to defer deriving the extra fields until later. Here, it looks like the work $file can be deferred until after checking if the frame doesn't exist.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Wouldn't abs_path and lineno be not unique with recursive calls?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Profiles are from the callee perspective not the caller right? If so I think abs_path and lineno would indeed be enough since in PHP we don't have monkey patching... so that would remove the need for hashing and we can also indeed skip some more work if we already have the frame.

Copy link
Member

Choose a reason for hiding this comment

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

Wouldn't abs_path and lineno be not unique with recursive calls?

Why wouldn't it be unique? It's the same function, just called recursively. So we can use the same frame index to represent it.

Copy link
Member

@cleptric cleptric left a comment

Choose a reason for hiding this comment

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

🪨

@cleptric cleptric merged commit 7f4e129 into master Jul 31, 2023
29 checks passed
@cleptric cleptric deleted the reuse-profiler-frames branch July 31, 2023 14:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants