Saturday, 25 July 2015

DLR vs InvokeDynamic

I found out the other day that PowerShell 3.0 makes use of the DLR. It came as a surprise, as after the abandonment by Microsoft of the Iron languages, I thought they were setting it aside, just keeping it in the framework but with no plans of using it for anything. Hopefully it's not like that, and I guess the reason for not hearing anything about changes or evolutions in the DLR in the last versions of the CLR is that it's complete enough and there's no pressing need for improvements.

This finding, along with this excellent article about invokedynamic in the Java Platform, made me think again of the differences between both approaches for speeding up dynamic languages in the JVM and the CLR. Last year I had already discussed in this previous article how Java 8 implements Lambdas by means of invokedynamic and generating at run time the classes needed to contain the state of the lambdas, on the other side either C#, Scala and Groovy (at least in the pre-Java 8 times) create these classes fully at compile time.

Let's forget about Java Lambdas and let's focus on the "dynamic performance boosting techniques". Both invokedynamic and the DLR make massive use of runtime code generation, but in rather different ways.

invokedynamic When the JVM interpreter first finds an invokedynamic bytecode instruction, it will call into a bootstrap method and will obtain a CallSite object wrapping a MethodHandle. So the invokedynamic instruction will be replaced by a call to that MethodHandle hosted in the CallSite. This call to the bootstrap happens only once for each invokedynamic instruction found. As the code evolves, the MethodHandle in the CallSite can be replaced by another MethodHandle. Regarding the bootstrapping process, I think this statement I found somewhere is pretty important for its understanding:

"the key thing about invokedynamic is that it's essentially a JVM-level macro that defers lambda translation strategy to LambdaMetaFactory which is a library class."

and from this other article:

Whenever a invokedynamic call site is created in bytecode, it references a so-called bootstrap method for deciding what method should be invoked. A bootstrap method is implemented in plain Java and serves as a lookup routine for locating the method that should be called for the invokedynamic instruction. This lookup is only performed a single time. It is however possible to manually rebind a dynamic call site at a later time.

So the key part now is understanding what a MethodHandle is. Well, it's basically an object containing a "method pointer". So, it's basically the same as a .net delegate? yes and no. In .net we have multiple delegate types, classes that inherit from System.MulticastDelegate. We create (well, since .net 2.0 we normally use the Action and Func ones provided by the Framework) delegate types for the different method signatures that we need. This way, the invokation of a delegate is type safe and fast, as the the type correctness is guaranteed at compile time. In Java we have a unique MethodHandle class, so this type safety is guaranteed differently. The JVM itself knows about MethodHandles, and they are invoked through what is known as a "polymorphic signature", so it allows to call MethodHandle.invokeExact with different parameters in the different call sites.

DLR. The procedure to improve .net as a host for dynamic languages did not entail the creation of any new bytecode instruction or changes to the Virtual Machine, but a rather smart use of existing features like Expression Trees, Delegates and LightWeight Code Generation. This series of posts about the DLR internals gives and excellent explanation. Summing it up, in those locations where a dynamic call is performed (for example through the use of a variable declared as dynamic in C#) the corresponding compiler will insert the creation of a CallSite object and will invoke the delegate referenced by its Target property. So, basically this sounds just very similar to the CallSite-MethodHandle combo in the Java Platform. The CallSite creation in .net is done at compile time and in Java at runtime, so looks like the Java approach has to be more dynamic. Well, not really, the CallSite creation in .net is the less important part, the juicy thing happens at runtime, when depending on what object we really have at that point each time, dynamic code generation is used to create a new delegate (doing heavy use of a cache system) to bind to the CallSite.Target. This is pretty sophisticated and is explained here. I assume the JVM uses a similar Cache and decision system for rebinding the MethodHandle in a CallSite, but I have not investigated about it.

I've read in some places that the JVM approach is better and hence it's better suited for dynamic languages. This is one of those sources. It basically says (I think) that in the JVM when finally native code is generated this can be inlined, but it's not like that for the CLR. Honestly, this is a too advanced topic for me, so I can not say how true and how an impact it really makes. Anyway, if not the best, I think the CLR + DLR have proved to be a rather good host for dynamic languages, and with this in mind (and with all their investment in TypeScript) I'd love to see Microsoft coming up with an implementation of ES5-ES6 for the CLR (like Oracle has done with Nashorn)

No comments:

Post a Comment