-
Notifications
You must be signed in to change notification settings - Fork 3k
Reduce pinning by preloading superclasses #50091
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
Conversation
This works by reducing the amount of time spent by the JVM calling back into the class loader, which is generally all pinned time. Instead of having the JVM recursively load supertypes of the loaded type, we do it ourselves on the Java side ahead of time so when the JVM loads the superclass, it is already present.
8a98c5b
to
171362b
Compare
...t-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/RunnerClassLoader.java
Show resolved
Hide resolved
This is beneficial only if the cost we will pay on C2 to warmup exceed the cost of the orignal JVM code (likely a mix of native calling into java and forth? I don't remember):
I'm checking the code in the meantime 🙏 |
...t-projects/bootstrap/runner/src/main/java/io/quarkus/bootstrap/runner/RunnerClassLoader.java
Outdated
Show resolved
Hide resolved
Thinking about it twice, I need to recap what pinning really is, at the lowest possible level.. And checking where the JVM currently issue a pinning event i.e.:
And, looking the stack trace at #40917 (comment) (which doesn't have monitor involved, it seems) it could be summarized in: The reason why is dangerous is that, similar to monipolization, it prevents the carrier to make progress and run other virtual threads. Now: does the existing class loader pin a virtual thread performing class loading to a carrier? Lines 150 to 151 in 8e97671
In short, in the new class loader we never park a virtual thread but we eventually try to define, failing with linkage error. Said that, I need to wrap my head better re-phrasing what this PR does: |
I think the tradeoff is likely to be different, but we'll have to measure to see for sure.
I think the big cost will be the initial I/O to load the class bytes. I think that iterating the class bytes is going to be a relatively small slice even when not fully warmed up. But only measuring will tell us for sure.
I'll comment on those threads separately. |
I'm not 100% sure I agree, or maybe I misunderstand you. In that stack trace, there are hidden frames (native IIRC): the JVM is late-linking a class method.
If the JVM calls I guess we could check to see if the current thread is virtual, and if so, check to see if it is pinned, and if so, skip doing the preload work because it won't help anyway. Like I said in another issue, the best fix for pinning is to probably somehow reschedule the virtual thread to a separate carrier pool before the JVM initiates the class load. Everything else is a bandage. |
Yep, I think I've written too much, making my point less clear 🙏
This is very true, but at the same time, since the reasons which make a preemption necessary are removed (we removed in the algorithm any blocking operations AFAIK), it shouldn't be a big deal, unless I'm not considering some other hidden blocking reason/contention. It's still a torrent of words from me, sorry, but I hope clarify a bit my point |
I think this probably won't improve much, since the vast majority of class loading cases are going to come from run-time linking, which will cause the thread to pin before we even get a chance to do anything. So, closing this one for now. We can revisit later if it turns out there's a bigger win than expected somehow. |
This works by reducing the amount of time spent by the JVM calling back into the class loader, which is generally all pinned time. Instead of having the JVM recursively load supertypes of the loaded type, we do it ourselves on the Java side ahead of time so when the JVM loads the superclass, it is already present.
See #43022 for more discussion on the topic.