-
Notifications
You must be signed in to change notification settings - Fork 2
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
Entity instantiation standard / optimized benchmark #13
Conversation
6f3147c
to
2f78f7e
Compare
Give me a couple of days @mbladel and will send a PR with the feedbacks! |
Ideally we would like to verify how much better is with a full fat findAll - i.e. something which looks more like a query which uses the istantiator(s) registered. |
Right - I thought we only wanted to measure instantiation performance difference so I replicated what Hibernate does internally anyway in the two cases. I'll experiment a bit more and see if I can switch to a query for the benchmark - though in that case I believe the difference in performance will be less noticeable among all the other operations taking place. |
2f78f7e
to
b55951e
Compare
@franz1981 I've updated the code to use a query instead and edited the original PR message with the new results and flamegraph. Kindly let me know if there's anything else I should change. |
basic/src/test/java/org/hibernate/benchmark/enhancement/EntityInstantiators.java
Outdated
Show resolved
Hide resolved
b55951e
to
8fcd146
Compare
@franz1981 here are the results of a run on my machine and JDK17 with MONO/QUAD morphism and Standard/Optimized variations:
The ops/s are almost identical in the QUAD case. We looked at the flamegraphs and the impact of The optimized strategy is still better in MONO case, but that's pretty synthetic as real-world applications would often query different entity types. |
8fcd146
to
4c7744a
Compare
4c7744a
to
cf41ead
Compare
Further testing with a bytecode enhanced entity (to alleviate the impact of https://hibernate.atlassian.net/browse/HHH-18763) and even with JDK24 showed no significant gain in total ops/s from instantiation optimizers due to the poor performance of the I'm going to merge this as it's a valuable benchmark in terms of simple query performance, instantiation and handling of new objects in the Persistence Context. |
I'm pasting here some cheap trick to use with care (but I want still you to be aware/play with it); if used right can deliver some very real improvement...
Similar to virtual calls, switches are profiled by the runtime - which means that branches can end up changing based on the observed frequencies - but similarly to "type profile information"s (see https://github.com/openjdk/jdk/blob/120a9357b3cf63427a6c8539128b69b11b9beca3/src/hotspot/share/opto/doCall.cpp#L83-L396 for what happen while the JIT generate a call) once a full compilation happen (i.e. at the latest and more refined level of compilation), their "final form" is shared across different caller(s) - which means that I expect they won't behave that worse from dynamic dispatch ones. |
Hey @franz1981, thanks for the tips. This is interesting, though for sure point 2 (number of types <= 10) is definitely very rare in a real-world application scenario. Having a single instantiator class for all entity mappings is something actually feasible in Quarkus, where we collect every mapped type at static-init time. Though seeing the results of reflection performance when it comes to constructors, especially since JDK > 17 with method handles, makes me wonder whether having any instantiation optimization (access optimizers are a very separate topic) is even worth it. |
Comparison between Hibernate's standard POJO instantiators using reflection (i.e.
java.lang.reflect.Constructor#newInstance
) vs the bytecode-enhanced optimization which enables direct usage of the class' no-arg constructor. The benchmark tests a very simple "find all" query which instantiates a lot of entities.Here are the results of a JMH run as an example:
Showing a ~2.6% increase in ops/s for the
optimized
case.We can clearly see the impact of the
Constructor.newInstance
reflective call when looking atstandard
case CPU flamegraphs:While it disappears when using the instantiation optimizer: