On 08/05/2012 04:54 PM, Rob N wrote:
Thanks.  It will take me a while to understand that code.  Once I do,
maybe I can write a performance test.  I'd like to see a comparison
between invokedynamic with guarded/chained method handles, as you
described, vs calling the dispatch logic the old fashioned way.

Rob

Hi Rob,
I'm the guy that write this piece of code an because I'm stuck in Salt Lake City airport for the next three hours,
I suppose I can write something about this code.

The implementation is based on two papers, [1] and [2] and
yes, I've written my Phd thesis on multi-method in Java a long time ago.

The first paper describes a way to do the multi-dispatch using a table based approach (like a vtable dispatch but more complex), so it provides a way to 'cover your ass', i.e. a way to have if no optimization tricks work to fallback to a dispatch algorithm which is linear to the number of parameter (so it doesn't depend on the number of implementations of the multi-method which is something important).

It works like this, let say we have two methods with B extends A,
  void foo(A a, A a2) { ... }  // impl1
  void foo(A a, B b) { ... }   // impl2

the algorithm first sort the methods using their types, here because B is more specific that A,
impl2 is more specific than impl1, so the method array is { impl2, impl1 }.

Then for each parameter index, you record in a hashmap for each available type,
if a method implementation is applicable (can be called).
Here, the hashmap for the first parameter contains only one key, A, and the corresponding bitset is 11 because impl2 and impl1 can be called if A is the first parameter.
Using the same algorithm for the second parameters you get:
   hash1 = { A -> 11 },
   hash2 = { A -> 01, B -> 11 }

now when the multi-dispatch is called, let say with an object of class B and another of class A,
  call.foo(B,A),
the first hashmap, hash1, doesn't contains B, in that case, follow the supertype hierarchy and try to find a supertype which is a key, here because B inherits from A, B will act as A,
so you can cache (for the next call) that B -> 11 in hash1.
the second parameter is A, so the hash entry is A -> 01,
The result is
call.foo(B,A) = {11 } & { 01} = 01, so method implementation at index 1 should be called -> impl1

so calling a multi-method should never be slower than accessing to the hashmap for each arguments,
do a bitwise-and and get the lower bit set (the more specific).

The real algorithm is more convoluted because you can have interfaces, so you can have more than one bitset for a class at runtime, when the array is sorted, you may have to introduce new implementation by example, for foo(A,B) and foo(B,A) the Java semantics require to include a third method foo(B,B) that will just throw an exception because foo(A,B) and foo(B,A) are not comparable.

Moreover, there is a bunch of special case that can be used to faster the algorithm. If there is only one implementation, multi-dispatch is not necessary, if there is several implementation but only one is ever call for a callsite (the callsite is monomorphic) a guard with test will be faster, (maybe it's also the case for bi-morphic, and so on if only the receiver class changed)

Also, if the language you want to implement is object oriented, then the number method applicables depends on the receiver class, you can use a GuardWithTest for this or consider the receiver class
as a parameter too.

Also, maybe for a parameter, there is only one possible class, for foo of our running example, the first parameter is always an A, in that case, it's better to not process a bitset for it but just do an instanceof check (or no check at all if you know that for a callsite the first parameter
is always an A.

Also, if you have only one parameter (one that can changed), in that case, it's better to not
compute the set but directly store the method handle in the hash.
For foo, it will be
  hash2 = { A -> impl1, B -> impl2 }
and to use the result of hashmap.get() with a fold + exactInvoker.

Also, for a specific callsite, you can have primitive type in the signature, in that case, you can pre-process the bitset that will be used and insert it as a constant (with a insertArguments).

Also, depending on your CPU, some provide an instruction for the highest bit, some for the number of trailing zeroes (latest AMD), so if the corresponding intrinsic exists in hotspot (I don't think it's true now but it may change) so you may have to sort the method in one order or
in reverse order.

So currently, as far as I remember, I've implemented the bitset algorithm but only
if there is less than 32 implementation, so store the bitset on an int.
The hash is thread-safe but it use a splay-tree to avoid to consume to much memory but it may be better for performance to use a hashmap (without a volatile access
if there is no cache miss).
Note that there is two flavor of the hash, one if there is no interface, so calling getSuperclass()
is enough and a second one works with interfaces.

And I've also implemented all the tricks listed above.

cheers,
Rémi

[1] http://webdocs.cs.ualberta.ca/~paullu/Papers/COOTS2001-HTML/mdj.html
[2] http://www.jot.fm/issues/issue_2005_12/article3/


On Aug 5, 3:18 am, Charles Oliver Nutter <head...@headius.com> wrote:
A simple way to do what you want might be sketched out like this:

* The bootstrap method returns a CallSite + handle for your
multi-dispatch logic contained in another method.
* The multi-dispatch logic receives the actual arguments for that first call.
* Based on those arguments, you build a guarded chain of method
handles that calls the right target for the incoming types, and falls
back on the multi-dispatch lookup logic again if new types come in.

 From here, you can do various combinations of guards, multiple
targets, mechanisms for quickly calculating whether the incoming types
have already been negotiated, and so on.

You want to look at the bootstrap as your way to point future calls
toward your own specialized logic; the bootstrap does not necessarily
contain that logic.

There's an example implementation of multi-dispatch 
here:http://code.google.com/p/jsr292-cookbook/source/browse/trunk/multi-di...

- Charlie







On Sat, Aug 4, 2012 at 11:26 PM, Rob N <rob.nikan...@gmail.com> wrote:
Hi,
I'm trying to learn about invokedynamic and the supporting classes,
and I don't yet see how they help implement the dynamically typed
language I have in mind. From what I've read so far, when you emit an
invokedynamic instruction, you specify a bootstrap method in your
language's runtime, but that is only run once, and it must return a
MethodHandle for the CallSite.  Lets say I wanted to emit something
for a CLOS-like multiple dispatch method (foo x y), that will select
an implementation method based on the classes of x and y.  Would I not
have to return, from the bootstrap method, a handle to a method that
did that dispatch?  So why not just emit an invokevirtual or
invokestatic directly to that method?  What is the advantage of
invokedynamic here?
thanks,
Rob
--
You received this message because you are subscribed to the Google Groups "JVM 
Languages" group.
To post to this group, send email to jvm-languages@googlegroups.com.
To unsubscribe from this group, send email to 
jvm-languages+unsubscr...@googlegroups.com.
For more options, visit this group 
athttp://groups.google.com/group/jvm-languages?hl=en.

--
You received this message because you are subscribed to the Google Groups "JVM 
Languages" group.
To post to this group, send email to jvm-languages@googlegroups.com.
To unsubscribe from this group, send email to 
jvm-languages+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/jvm-languages?hl=en.

Reply via email to