Repository navigation
Avoid resolving a MethodHandle for every event listener invocation - #747
BonsUnleashed wants to merge 1 commit into
Conversation
|
Would storing the method handle produce the same level of performance gains? i.e. I think the original idea to unreflect was to reduce stacktrace reflection traces |
|
Storing the unreflected handle alone doesn't get there, because
On JDK 21 the cached spreader is about as fast as |
|
The cached spreader option sounds good, would appreciate if you can switch to that! Thanks! |
96cd7e4 to
aab2410
Compare
|
Thanks for the suggestion! Switched in aab2410: one Same microbenchmark as above, 5M calls, best of 5:
The map lookup costs about 4 ns over a static handle. A 30-case comparison against the original (return values with boxing, unboxing and void; thrown objects by identity; null receiver) passes on both JDKs. Description updated, compiles on common and forge. Thanks again, it's the cleaner version. |
EventFactory.invokeMethodrunsMethodHandles.lookup().unreflect(method).bindTo(listener).invokeWithArguments(args)for every listener call, so each call looks up, binds and adapts a new handle. This keeps one handle perMethodin aConcurrentHashMap: unreflected once, adapted withasSpreaderandasTypeto(Object, Object[])Object, and called withinvokeExact. Arguments, return values and listener exceptions reach callers as before, and the stack trace between a listener andinvokeMethodhas no reflection frames.In a JDK 17 microbenchmark with one listener and two arguments, a call went from about 860 ns and 1.5 KB to about 18 ns with no allocation (JDK 21: about 800 ns to 18 ns).
Compiles on
1.20(common and forge). Not tested in game on this branch.Bons and Furious currently ships a patch for this: CurseForge · Modrinth · GitHub