Skip to content

Make private & internal stuff sealed #2915

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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

Henr1k80
Copy link

No description provided.

@mgravell
Copy link
Collaborator

There's a part of the discussion missing here perhaps - what is the motivation for this? Sure, we can do it and it doesn't change the API, but: is there something specific we're doing it for? Note that none of this seems to be likely to be areas where the JIT will care to do anything different for sealed (and even if it did: nothing there is a CPU hot path).

@Henr1k80
Copy link
Author

It results in less IL, as the virtual dispatch is no longer needed, and makes the functions candidates for inlining:
dotnet/runtime#49944

The exception is .NET Framework, where it has no effect.

I admit that I have not done any performance analysis of this, but the motivation is correctness and presumed that any performance gains would be welcome, albeit probably hardly measurable.
The sealed keyword is already in heavy use elsewhere, so it can as well be streamlined, maybe even consider adding CA1852

@Henr1k80
Copy link
Author

Henr1k80 commented Jul 21, 2025

One more thing, it removes the jmp, where the branch predictor has to guess where to load the next instructions from.
If there is a mispredict, the speculative loaded code & data must be discarded and correct code & data loaded, a pipeline stall.
Pipeline stalls are more expensive than the few instructions added by a virtual dispatch.

Branch predictors are very good, but by removing the jmp, there is one less branch to track.
Branches to track is not only in this code, but in the entire application using this library.
The more the branch predictor has to keep track of, the worse the performance: https://blog.cloudflare.com/branch-predictor/

@mgravell
Copy link
Collaborator

I'm very aware of the potential advantages of devirtualization; however, whether such things are useful is hugely contextual. Yes, there are some tight loop CPU bound scenarios that might be meaningful in hot code paths, but unless I'm very mistaken, none of that is remotely relevant to any of the situations presented. When suggested arbitrarily without concrete applicable reasons, this ... just seems ... cargo cult programming. I don't mean to be dismissive or disparaging - but I'm also not a fan of changes without demonstrable reasons.

@Henr1k80
Copy link
Author

I think the comprehensional cost of adding the sealed keyword is minimal.
So little, that it is not worth proving that it makes a difference.
The IL is smaller, has no branches and makes it possible to inline. Do it always, no discussion needed.
It is not like I am introducing mediator pattern or something, there is not added any new code.

If just a single virtual dispatch can be avoided for a call to redis, it is worth it, to avoid polluting the branch predictors & caches with unneeded stuff.
Scale it with the popularity of this library worldwide.
I expect the libraries I use to be as optimal as can be.

# Conflicts:
#	src/StackExchange.Redis/ResultProcessor.cs
@mgravell
Copy link
Collaborator

mgravell commented Jul 21, 2025

no discussion needed.

That's not how anything works.

The IL is smaller, has no branches and makes it possible to inline

The IL is identical; it emits the same callvirt in all cases; any devirtualization is done at the JIT, and is dependent on many factors, and I'm pretty sure those rules mean any optimizations here will never apply. Likewise, since you are leaning on "optimal": even if the JIT changes did apply: it is irrelevant to performance here - similar to my comments here.

Note also that the JIT can often sees through virtual without needing the sealed change.

I guess it comes back down to:

There's a part of the discussion missing here perhaps - what is the motivation for this?

The conversation so far has focused on "optimal"; this change will have zero effect on that (or so close to zero that it is negligible). That means we're left with the implicit unstated reason (one of):

  • because (some tool) told me to add sealed
  • because it can be sealed

These are probably presumably more honest. To quote Phil Davis (White Christmas):

Well, it's not good, but it's a reason.

I do think it is important to be clear about why we're proposing / making any change, and the impact we expect it to have.

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.

2 participants