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

Make some constant SubscribableListener instances cheaper #124452

Conversation

original-brownbear
Copy link
Member

We can just use a real constant for the null case, avoiding any non-plain stores in all cases. This should be somewhat helpful for the security interceptors.
Also, the spots where we create completed instances can use weaker release-type memory semantics to potentially help the compiler out.

We can just use a real constant for the `null` case, avoiding any
non-plain stores in all cases. This should be somewhat helpful for the
security interceptors.
Also, the spots where we create completed instances can use weaker
release-type memory semantics to potentially help the compiler out.
@elasticsearchmachine elasticsearchmachine added the Team:Core/Infra Meta label for core/infra team label Mar 9, 2025
@elasticsearchmachine
Copy link
Collaborator

Pinging @elastic/es-core-infra (Team:Core/Infra)

Copy link
Contributor

@DaveCTurner DaveCTurner left a comment

Choose a reason for hiding this comment

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

I'm ok with the nullSuccess change, not so much with the setRelease.

* Create a {@link SubscribableListener} which has already succeeded with the given result.
*/
public static <T> SubscribableListener<T> newSucceeded(T result) {
var res = new SubscribableListener<T>();
Copy link
Contributor

Choose a reason for hiding this comment

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

This allocates a new Object() for the initial state, can we find a way not to do that?

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah 🤦 gotta fix that too, otherwise this is kinda pointless. Sec on it

Copy link
Member Author

Choose a reason for hiding this comment

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

Alright, not sure you'll like this one :) But if I get rid of EMPTY this all falls into place just fine doesn't it?

*/
public static <T> SubscribableListener<T> newSucceeded(T result) {
var res = new SubscribableListener<T>();
VH_STATE_FIELD.setRelease(res, new SuccessResult<>(result));
Copy link
Contributor

Choose a reason for hiding this comment

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

Hm so my understanding of setRelease is a little sketchy but AIUI it blocks earlier stores/loads from moving later but does not block later stores/loads from moving earlier. Is that valid here? It seems like if we did something like this ...

l = SubscribableListener.newSucceeded(r1);
// make l visible to another thread
l.onResponse(r2);

... then potentially someone could observe r2 rather than r1?

OTOH if there's something magic about newly-constructed objects that makes this work, does the same reasoning not also work for all volatile writes in a constructor (as long as it doesn't have let this escape I guess)? If so, why is the compiler not already doing this?

Basically this feels risky enough that I'd like to see hard data about the benefits, and a big old comment proving its correctness.

Copy link
Member Author

Choose a reason for hiding this comment

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

This will not be a problem because you do a volatile mode read on the state field in onResponse to check if it isn't done already. So that later store never actually happens here.

does the same reasoning not also work for all volatile writes in a constructor

There is no magic for newly constructed objects actually. See https://shipilev.net/blog/2016/close-encounters-of-jmm-kind/#wishful-volatiles-are-finals :)

Basically this feels risky enough that I'd like to see hard data about the benefits,

That's probably impossible to come by :) It's dependant on target architecture, JVM version and all of that stuff.
For me it's just, the more we use this primitive, the more likely we are to create a spot where it's advantageous to have some operations reordered to before the creation of these already-complete instances. Also as compilers make progress this becomes more likely :)

Copy link
Contributor

@DaveCTurner DaveCTurner left a comment

Choose a reason for hiding this comment

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

Ok can we do just the nullSuccess change first and then come back to the weak memory semantics separately?

@original-brownbear
Copy link
Member Author

Sure thing, backed everything else out for now :) That's the only change that I can see a measurable improvement on transport threads for :) (from security, could probably take this further and add some more constants in security in a follow-up)

Copy link
Contributor

@DaveCTurner DaveCTurner left a comment

Choose a reason for hiding this comment

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

LGTM

@original-brownbear original-brownbear added the auto-backport Automatically create backport pull requests when merged label Mar 9, 2025
@original-brownbear
Copy link
Member Author

Thanks David!

@original-brownbear original-brownbear merged commit 29ac261 into elastic:main Mar 9, 2025
17 checks passed
@original-brownbear original-brownbear deleted the constant-subscribable-listeners branch March 9, 2025 21:32
@elasticsearchmachine
Copy link
Collaborator

💔 Backport failed

Status Branch Result
8.x Commit could not be cherrypicked due to conflicts

You can use sqren/backport to manually backport by running backport --upstream elastic/elasticsearch --pr 124452

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
auto-backport Automatically create backport pull requests when merged backport pending :Core/Infra/Core Core issues without another label >non-issue Team:Core/Infra Meta label for core/infra team v8.19.0 v9.1.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants