MethodHandle Performance

Last time I mentioned that with the integration of OpenJDK 8u45 MethodHandle performance went from awful to unusable. That was pretty literal as the JSR 292 test cases that I regularly run went from taking about 8 minutes to more than 30 minutes (when my patience ran out).

Using sophisticated profiling techniques (pressing Ctrl-Break a few times) I determined that a big part of the problem was MethodHandle.asType(). So I wrote a microbenchmark:

IKVM 8.0.5449.1 IKVM 8.1.5638 asType.permutations(1) 2108 9039 asType.permutations(2) 2476 17269

The numbers are times in milliseconds. Clearly not a good trend. I did not investigate deeply what changed in OpenJDK, but after looking at the 8u45 code it was clear that too many intermediate MethodHandles were being created. So I rewrote asType to create a single LambdaForm to do all the work at once. This improved the performance a bit, but the disturbing increase in time for the second iteration was still there. Once again I decided not to investigate the root cause of this, but simply to assume that it was because of anonymous type creation (the CLR has no anonymous types and creating a type is relatively expensive).

Avoiding anonymous type creation turned out to be easy (well, the high level design was easy, the actual implementation took a lot more time). I just had to replace the LambdaForm compiler. There is a single method that represents the exact point where I can come in and change the implementation:

static MemberName generateCustomizedCode(LambdaForm form, MethodType invokerType) { ... }

In OpenJDK this method compiles the LambdaForm into a static method in an anonymous class and returns a MemberName that points to the static method. All I had to do was replace this method with my own implementation that directly generates a .NET DynamicMethod. As I said before, the idea was simple, actually getting the implementation correct took a couple of weeks (part time).

With both these optimizations in place, MethodHandle performance is back to awful (actually, it is less afwul than it was before):

IKVM 8.0.5449.1 IKVM 8.1.5638 IKVM 8.1.5653 asType.permutations(1) 2108 9039 314 asType.permutations(2) 2476 17269 210

The running time of the JSR 292 test cases went down to less than 7 minutes. So I was satisfied. There are many more opportunities to improve the MethodHandle performance on IKVM, but so far no IKVM user has complained about it, so it is not a priority. Note that Java 8 lambdas are not implemented using MethodHandles on IKVM.

Changes:

Fixed performance bug. Base type of java.lang.Object was not cached.

Untangled TypeWrapper.Finish() from member linking to improve Finish performance for already compiled types.

Improved MethodHandle.asType() performance by directly creating a single LambdaForm to do the conversion, instead of creating various intermediate forms (and MethodHandles).

Make non-public final methods defined in map.xml that don't override anything automatically non-virtual.

Optimized LambdaForm compiler.

IKVM.Reflection: Added Type.__GetGenericParameterConstraintCustomModifiers() API.

Binaries available here: ikvmbin-8.1.5653.zip