-
Notifications
You must be signed in to change notification settings - Fork 54
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Java.Interop, Java.Interop.Dynamic] Array Performance Investigation
Remember commit 775b34f? > Con: OMFG IS THIS SLOW. > > Like, wow, incredibly slow, like ~11s to lookup the names and > parameter types of all methods on java.util.Arrays. So... WTF is going on there? Object identity is what's going on there. For "sane"/"correct"/"normal" semantics, JavaObjectArray<T> follows object identity semantics, e.g. JavaVM.GetObject() will always return the same (registered) wrapper for Java handle that refers to the "same" Java instance, as per JavaVM.RegisteredInstances. Supporting Object identity is...not fast. See the new JavaArrayTiming.ObjectArrayEnumerationTiming() and MiscRuntimeTiming.ObjectCreationTiming() tests. ObjectArrayEnumerationTiming() uses Java reflection to grab all of the methods of java.util.Arrays. The stunning output? # methodHandles(JavaObjectArray<JavaObject>) creation timing: 00:00:00.6106453 Count=114 # methodHandles(JavaVM.GetObject) creation timing: 00:00:00.5959359 Count=114 # methodHandles(JavaObject[]) creation timing: 00:00:00.0890776 Count=114 # methodHandles(JniGlobalReference) creation timing: 00:00:00.1536897 Count=114 Given a JNI handle to the java.lang.reflect.Method[] returned by Class.getMethods(), the above shows four different ways of reading that Method[] and turning it into a List<Something>. The JavaObjectArray<JavaObject> implementation takes 0.61 *seconds* just to grab each element from the JavaObjectArray<JavaObject> and copy it into a List<JavaObject>. For 115 items. Which is *deplorable*. Why is it so bad? Next is an implementation which grabs the JNI handle for each element in the array and passes it through JavaVM.GetObject<T>() before adding to a List<JavaObject>. Note that it's VERY CLOSE in time to the JavaObjectArray<T> version. The methodHandles(JavaObject[]) version grabs each element from the array, then immediately constructs a new JavaObject instance around it. NO OBJECT IDENTITY is involved; aliases can (will) be created, and that's OKAY (here). Note that we drop from ~0.6s to ~0.089s -- nearly 7x faster, simply by skipping object identity. The final methodHandles(JniGlobalReference) is the same-but-different as methodHandles(JavaObject[]): instead of a new JavaObject, it greates a new JNI Global Reference. (Why a GREF? Futureproof!) It's slower than JavaObject (must be Local references vs. Global refs), but still much faster than our object identity infrastructure. TL;DR: Object identity is problematic. There is an issue to attempt to address it: #4 ...but we're not fixing that here. The ObjectCreationTiming() tests are a different take on the above results, comparing the timing of JNIEnv::AllocObject(), JNIEnv::NewObject(), `new JavaObject()`, and JavaVM.GetObject<JavaObject>(): ## ObjectCreationTiming Timing: Total=00:00:35.1368016 AllocObject=00:00:02.8751501 NewObject=00:00:02.8724492 `new JavaObject()`=00:00:06.0586476 JavaVM.GetObject()=00:00:14.5206758 This somewhat agrees with the previous discussion: JavaVM.GetObject<T>() (object identity) is slower than direct object creation. With that in mind, we can turn our attention back to Java.Interop.Dynamic and its terrible performance by replacing it's JavaObjectArray<JavaObject> use with low-level array access. This in turn necessitates making the JniEnvironment.Arrays type public (previously `internal`) so that Java.Interop.Dynamic can skip/bypass the object identity semantics that JavaObjectArray<T> enforces. This improves things from taking ~11s to lookup the names to taking ~4s to run the entire Java.Interop.Dynamic-Tests assembly. Total time : 3.9523533 seconds This is much more reasonable. (Perhaps not perfect, but reasonable.) While investigating, fix a variety of other minor issues: * Fix JavaObjectArray<T> so that it doesn't needlessly re-access the Length property all the damn time, which SHOULD reduce JNI calls and thus improve performance. * Fix JavaVM.CreateWrapperType() to not create so many JniType instances. Investigation was suggesting that the JniType creation and disposal was adding a bit of overhead, so avoiding the JniType abstraction provides *some* (small) speedup. * Cleanup JniType.Name (commit 9f380eb), moving its logic into JniEnvironment.Types.GetJniTypeNameFromClass(). This further increases consistency.
- Loading branch information
Showing
8 changed files
with
280 additions
and
60 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.