-
Notifications
You must be signed in to change notification settings - Fork 785
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
[sdk-metrics] Remove ExemplarFilter classes #5408
[sdk-metrics] Remove ExemplarFilter classes #5408
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #5408 +/- ##
==========================================
+ Coverage 83.38% 84.73% +1.34%
==========================================
Files 297 281 -16
Lines 12531 12078 -453
==========================================
- Hits 10449 10234 -215
+ Misses 2082 1844 -238
Flags with carried forward coverage won't be shown. Click here to find out more.
|
} | ||
catch (Exception) | ||
|
||
switch (this.exemplarFilter) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would probably be faster to use if
than switch
? If yes, we should optimize the non-exemplar updates.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Found an old benchmark saying that switch usually will be faster cause compiler will create jump table for switch statement: https://www.blackwasp.co.uk/SpeedTestIfElseSwitch.aspx.
I guess the assembly generated for if/switch here will be identical given there are only 3 cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you know your most common condition, you can make that the very first check. That way you only have to check if(true)
to execute the most common path. That might be faster than the jump table lookup. In fact, you can use a mixed approach as well. Something like this:
if (mostFrequentlyHitCondition)
{
RunSomething();
}
switch (lessFrequentlyHitConditions)
{
case Condition1:
case Condition2:
...
default:
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I updated the code to use an if
block. But it has been interesting to try and prove what is best.
Here are some benchmarks run without PGO enabled:
Method | ExemplarFilterType | Mean | Error | StdDev | Allocated |
---|---|---|---|---|---|
UpdateOldStyle | AlwaysOff | 2.458 ns | 0.0722 ns | 0.0676 ns | - |
UpdateInlineSwitchStyle | AlwaysOff | 1.572 ns | 0.0366 ns | 0.0342 ns | - |
UpdateInlineIfStyle | AlwaysOff | 1.353 ns | 0.0457 ns | 0.0626 ns | - |
UpdateOldStyle | AlwaysOn | 2.938 ns | 0.0206 ns | 0.0172 ns | - |
UpdateInlineSwitchStyle | AlwaysOn | 1.800 ns | 0.0343 ns | 0.0304 ns | - |
UpdateInlineIfStyle | AlwaysOn | 1.800 ns | 0.0366 ns | 0.0342 ns | - |
UpdateOldStyle | TraceBased | 4.666 ns | 0.0629 ns | 0.0588 ns | - |
UpdateInlineSwitchStyle | TraceBased | 3.297 ns | 0.0470 ns | 0.0417 ns | - |
UpdateInlineIfStyle | TraceBased | 3.198 ns | 0.0382 ns | 0.0319 ns | - |
Here are some benchmarks run with PGO enabled:
Method | ExemplarFilterType | Mean | Error | StdDev | Allocated |
---|---|---|---|---|---|
UpdateOldStyle | AlwaysOff | 1.299 ns | 0.0410 ns | 0.0383 ns | - |
UpdateInlineSwitchStyle | AlwaysOff | 1.345 ns | 0.0374 ns | 0.0313 ns | - |
UpdateInlineIfStyle | AlwaysOff | 1.106 ns | 0.0316 ns | 0.0296 ns | - |
UpdateOldStyle | AlwaysOn | 1.359 ns | 0.0141 ns | 0.0132 ns | - |
UpdateInlineSwitchStyle | AlwaysOn | 1.373 ns | 0.0514 ns | 0.0481 ns | - |
UpdateInlineIfStyle | AlwaysOn | 1.325 ns | 0.0310 ns | 0.0275 ns | - |
UpdateOldStyle | TraceBased | 2.756 ns | 0.0709 ns | 0.0663 ns | - |
UpdateInlineSwitchStyle | TraceBased | 3.392 ns | 0.0452 ns | 0.0422 ns | - |
UpdateInlineIfStyle | TraceBased | 2.234 ns | 0.0231 ns | 0.0204 ns | - |
PGO is smart enough to see that one value is used repeatedly and optimize the code on its own for that value. It is great for what we're doing here.
I ended up going with the if
because PGO may not be there in AOT deployments/older runtimes so it seems best to optimize for it not being there.
…CodeBlanch/opentelemetry-dotnet into sdk-metrics-removefilterclasses
} | ||
catch (Exception) | ||
|
||
switch (this.exemplarFilter) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Found an old benchmark saying that switch usually will be faster cause compiler will create jump table for switch statement: https://www.blackwasp.co.uk/SpeedTestIfElseSwitch.aspx.
I guess the assembly generated for if/switch here will be identical given there are only 3 cases.
Follow-up to #5404
Changes
ExemplarFilter
class and the implementations.AggregatorStore
to useExemplarFilterType
inline.Benchmarks
I tried a bunch of different things to try and squeeze as much perf out of things as I could. Seems like doing a simple inline switch is the best:
Code
Merge requirement checklist