Method.invoke() vs. coded equivalents

2 views
Skip to first unread message

John Cowan

unread,
May 6, 2009, 11:26:50 AM5/6/09
to jvm-la...@googlegroups.com
In my language, I need objects which represent Java static methods.
My original plan was to generate code like this.

public class Function3 {
public static final int FOO = 1;
public static final int BAR = 2;
public static final int BAZ = 3;

private int which = 0;

public static final Function3 foo = new Function3(FOO);
public static final Function3 bar = new Function3(BAR);
public static final Function3 baz = new Function3(BAZ);

private Function3(which) { this.which = which; }

private static Object foo(arg1, arg2, arg3) = { ... }
private static Object bar(arg1, arg2, arg3) = { ... }
private static Object baz(arg1, arg2, arg3) = { ... }

public Object invoke(arg1, arg2, arg3) {
switch (which) {
case FOO: foo(arg1, arg2, arg3); break;
case BAR: bar(arg1, arg2, arg3); break;
case BAZ: baz(arg1, arg2, arg3); break;
default: throw new MethodInvocationError("can't happen");
}
}
}

Now I'm wondering if I shouldn't just use Method objects and
Method.invoke(). I know in the bad old days that was very slow, but
how fast is it on modern JVMs compared to the above? A
one-class-per-function solution is, I think, prohibitive.


--
GMail doesn't have rotating .sigs, but you can see mine at
http://www.ccil.org/~cowan/signatures

John Wilson

unread,
May 6, 2009, 11:38:56 AM5/6/09
to jvm-la...@googlegroups.com
2009/5/6 John Cowan <johnw...@gmail.com>:

>
> In my language, I need objects which represent Java static methods.


How about:

public abstract class Function3 {
public static final Function3 foo = new Function3() {
public Object invoke(Object arg1, Object arg2, Object arg3) {
return foo(arg1, arg2, arg3);
}
};

public static final Function3 bar = new Function3() {
public Object invoke(Object arg1, Object arg2, Object arg3) {
return bar(arg1, arg2, arg3);
}
};

public static final Function3 baz = new Function3() {
public Object invoke(Object arg1, Object arg2, Object arg3) {
return baz(arg1, arg2, arg3);
}
};

private static Object foo(Object arg1, Object arg2, Object arg3) = { ... }
private static Object bar(Object arg1, Object arg2, Object arg3) = { ... }


private static Object baz(arg1, arg2, arg3) = { ... }

public abstract Object invoke(Object arg1, Object arg2, Object arg3);
}


John Wilson

John Cowan

unread,
May 6, 2009, 1:33:16 PM5/6/09
to jvm-la...@googlegroups.com
On Wed, May 6, 2009 at 11:38 AM, John Wilson <tugw...@gmail.com> wrote:
>
> 2009/5/6 John Cowan <johnw...@gmail.com>:
>>
>> In my language, I need objects which represent Java static methods.
>
>
> How about:
>
>  public abstract class Function3 {
>  public static final Function3 foo = new Function3() {
>          public Object invoke(Object arg1, Object arg2, Object arg3) {
>                  return foo(arg1, arg2, arg3);
>          }
>  };

This is just the one-class-per-function design under a new disguise.

John Wilson

unread,
May 6, 2009, 2:13:41 PM5/6/09
to jvm-la...@googlegroups.com
2009/5/6 John Cowan <johnw...@gmail.com>:

>
> On Wed, May 6, 2009 at 11:38 AM, John Wilson <tugw...@gmail.com> wrote:
>>
>> 2009/5/6 John Cowan <johnw...@gmail.com>:
>>>
>>> In my language, I need objects which represent Java static methods.
>>
>>
>> How about:
>>
>>  public abstract class Function3 {
>>  public static final Function3 foo = new Function3() {
>>          public Object invoke(Object arg1, Object arg2, Object arg3) {
>>                  return foo(arg1, arg2, arg3);
>>          }
>>  };
>
> This is just the one-class-per-function design under a new disguise.


OK.

I have found that the reflection is about an order of magnitude slower
that direct method calls using JDK 6 with the -server option. However
the overhead is quite sensitive to the path from the call site to the
call of Method.invoke(). The JIT appears to like as few stack frames
as possible between the call site and the reflection call.

Object doCall() {
myMethod.invoke();
}

Method getMyCall() {
return myCall;
}

...
doCall() // slower

getMyCall().invoke() // faster
...

In my runtime the dispatch could happen three of four levels down and
returning the Method made a difference. If you are only one level down
you may not see a big advantage.

Also note that the performance of reflection has got better in later
JDKs so if there is a change that your language might have substantial
use on older JVMs then reflection becomes less interesting.

My experience is that you have to spend considerable energy in looking
at the execution paths through the runtime system ensuring that they
are simple (the prime requirement) and short ( a secondary
requirement).

One example:

I have a situation in which monkey patching can change the destination
of a call. In the MetaClass object which determines the behaviour of
the class I have a reference to a MetaClass which is used when there
has been a monkey patch to point to the new behaviour.

Originally I wrote this:

class MetaClass {

private MetaClass actualMetaClass;

public MetaClass() {
this. actualMetaClass = this;
}

public monkeyPatch(MetaClass newMetaClass) {
this.actualMetaClass = newMetaClass;
}

public Object invokeMethod() {
return this.actualMetaClass.doInvokeMethod();
}

public Object doInvokeMethod() {....


This was OK but rewriting the code like this:

class MetaClass {

private MetaClass actualMetaClass = null;

public monkeyPatch(MetaClass newMetaClass) {
this.actualMetaClass = newMetaClass;
}

public Object invokeMethod() {
if (this.actualMetaClass == null) {
return doInvokeMethod();
} else {
return this.actualMetaClass.doInvokeMethod();
}
}

public Object doInvokeMethod() {....


Made a substantial improvement in performance.

It appears that the JIT compiler can do more inlining in the second case.

If I was able (and, in general I'm not) I'd use one class per function
if I wanted the very best performance.


John Wilson

John Rose

unread,
May 6, 2009, 2:37:54 PM5/6/09
to jvm-la...@googlegroups.com
On May 6, 2009, at 8:26 AM, John Cowan wrote:

> In my language, I need objects which represent Java static methods.

That's what method handles are for; from a JDK 7 viewpoint you are
asking what is the best pre-JDK7 way to emulate method handles.

Rémi Forax has a backport project which will reweave your JDK7
bytecodes into a form suitable for pre-7 JVMs.

That pre-7 form generated by the backport uses about the same
expedients you are currently using. The nice thing about that is it
can serve as a collection point for best practices. I think grouping
methods in a switch is the state of the art before JDK 7.

Depending on your schema of call types, you'll want to choose between
grouping call types on interface types vs. one call type per interface
type. Interface types can be true Java interfaces, or Java abstract
classes with factored default error-reporting or adapting behaviors.

Any of those choices is usually preferable to plain reflection,
because the semantics of reflective invocation is very complex, as it
includes:
- wrapped individual arguments and return values
- wrapped varargs list
- wrapped exceptions
- per-call access checks with associated exceptions

Also, reflection does not support the semantics of invokespecial. And
there may be a footprint cost, as reflective objects are not mere
invocation capabilities (like method handles). They carry extra
baggage around to support symbol table queries such as annotations,
throws clauses, and Java generic signatures.

I hope this helps.
-- John

Robert Fischer

unread,
May 6, 2009, 2:52:53 PM5/6/09
to jvm-la...@googlegroups.com
Where can I find documentation on the JDK 7 changes to bytecodes and the backport project?

~~ Robert.
--
~~ Robert Fischer.
Grails Training http://GroovyMag.com/training
Smokejumper Consulting http://SmokejumperIT.com
Enfranchised Mind Blog http://EnfranchisedMind.com/blog

Check out my book, "Grails Persistence with GORM and GSQL"!
http://www.smokejumperit.com/redirect.html

Chanwit Kaewkasi

unread,
May 6, 2009, 2:56:11 PM5/6/09
to jvm-la...@googlegroups.com

Rémi Forax

unread,
May 6, 2009, 3:52:42 PM5/6/09
to jvm-la...@googlegroups.com
Chanwit Kaewkasi a écrit :
> Here's link to the backport code:
>
> http://code.google.com/p/jvm-language-runtime/source/browse/#svn/trunk/invokedynamic-backport
>
> Cheers,
>
> Chanwit
>
Currently, the backport is far from being usable :(

MethodHandle are supported if you don't have double slot arguments
(double or long)
in the signature.
User-defined MethodHandles are not supported.
There is no real support of invokedynamic, just a crude prototype.
The java.dyn package is not in sync with the latest pushes of John.

I have a plain text file with a plan saying how to correctly implement
all these things but I haven't find the time to correctly implement it.

Rémi

John Rose

unread,
May 6, 2009, 3:47:45 PM5/6/09
to jvm-la...@googlegroups.com
On May 6, 2009, at 11:52 AM, Robert Fischer wrote:

> Where can I find documentation on the JDK 7 changes to bytecodes and
> the backport project?

The next build of OpenJDK (b58) will contain a provisional API derived
on last year's JSR 292 EDR. The javadocs in that build will be useful.

The format of the invokedynamic bytecode has changed since last year's
EDR. There are no public specs. yet for the latest version, but (if
it helps) you can look at the javac backend for the invokedynamic
support, again in in OpenJDK build 58. The sources are viewable as of
this week:
http://hg.openjdk.java.net/jdk7/tl/langtools/file/tip/src/share/classes/com/sun/tools/javac/jvm/Code.java
(search for invokedynamic)

The 292 EG owes the world another EDR; I'm the bottleneck for that and
am working on that also.

-- John

John Cowan

unread,
May 6, 2009, 4:43:12 PM5/6/09
to jvm-la...@googlegroups.com
On Wed, May 6, 2009 at 2:37 PM, John Rose <John...@sun.com> wrote:

> I think grouping
> methods in a switch is the state of the art before JDK 7.

Okay. *sigh*

> Depending on your schema of call types, you'll want to choose between
> grouping call types on interface types vs. one call type per interface
> type.

Since the arguments and returns are all Object, I think all I need is
one class per arity: Function0, Function1, Function2, ... FunctionN,
which last takes an Object[] of arguments.

> Interface types can be true Java interfaces, or Java abstract
> classes with factored default error-reporting or adapting behaviors.

I don't follow you. Can you explain how this fits with the model I
posted? (Note that the static methods don't actually have to be in
this class at all; that was just the simplest thing pedagogically.) I
think you mean that the Functionx classes can be a subclass of an
abstract Function class which implements throwing versions of invoke0,
invoke1, invoke2, ... invoken, so as to catch calls with too many or
too few arguments. Is there something I'm missing?

Rémi Forax

unread,
May 9, 2009, 9:01:31 AM5/9/09
to jvm-la...@googlegroups.com, John Rose, Xiomara....@sun.com
John Rose a écrit :

> On May 6, 2009, at 11:52 AM, Robert Fischer wrote:
>
>
>> Where can I find documentation on the JDK 7 changes to bytecodes and
>> the backport project?
>>
>
> The next build of OpenJDK (b58) will contain a provisional API derived
> on last year's JSR 292 EDR. The javadocs in that build will be useful.
>
I seems there are some problem with b58 integration :
- the public javadoc doesn't contains java.dyn package.
http://download.java.net/jdk7/docs/api/

- javac doesn't want to find anything that is inside java.dyn:
fr/umlv/indy/visitor/test/Main.java:3: package java.dyn does not exist
import java.dyn.CallSite;

It's weird because java.dyn.* classes are in rt.jar.
And it works if the classpath is set to rt.jar
javac -source 1.7 -cp /usr/java/jre/lib/rt.jar:. -d ../build/classes/
fr/umlv/indy/visitor/test/Main.java

[...]

> -- John
>
Rémi

Robert Fischer

unread,
May 9, 2009, 11:06:38 AM5/9/09
to jvm-la...@googlegroups.com
John Cowan wrote:
>> Depending on your schema of call types, you'll want to choose between
>> grouping call types on interface types vs. one call type per interface
>> type.
>
> Since the arguments and returns are all Object, I think all I need is
> one class per arity: Function0, Function1, Function2, ... FunctionN,
> which last takes an Object[] of arguments.
>

I'd recommend having a Function<ARG_T extends ArgumentType> and abstract away the argument typing.

John Cowan

unread,
May 9, 2009, 1:38:09 PM5/9/09
to jvm-la...@googlegroups.com
On Sat, May 9, 2009 at 11:06 AM, Robert Fischer
<robert....@smokejumperit.com> wrote:
>
> John Cowan wrote:
>>> Depending on your schema of call types, you'll want to choose between
>>> grouping call types on interface types vs. one call type per interface
>>> type.
>>
>> Since the arguments and returns are all Object, I think all I need is
>> one class per arity: Function0, Function1, Function2, ...  FunctionN,
>> which last takes an Object[] of arguments.
>>
>
> I'd recommend having a Function<ARG_T extends ArgumentType> and abstract away the argument typing.

The language is dynamically typed, so Object really is all there is.

>
> ~~ Robert Fischer.
> Grails Training        http://GroovyMag.com/training
> Smokejumper Consulting http://SmokejumperIT.com
> Enfranchised Mind Blog http://EnfranchisedMind.com/blog
>
> Check out my book, "Grails Persistence with GORM and GSQL"!
> http://www.smokejumperit.com/redirect.html
>
> >
>



Robert Fischer

unread,
May 10, 2009, 3:59:00 PM5/10/09
to jvm-la...@googlegroups.com
When I say "argument typing", I meant having a single "function" class and leaving the number of
arguments to being an implementation detail handled by the type of the argument class. The reason
is because function classes usually do some fairly heavy lifting which shouldn't be repeated for the
N number of functions.

~~ Robert.

John Cowan

unread,
May 10, 2009, 5:15:49 PM5/10/09
to jvm-la...@googlegroups.com
On Sun, May 10, 2009 at 3:59 PM, Robert Fischer
<robert....@smokejumperit.com> wrote:
>
> When I say "argument typing", I meant having a single "function" class and leaving the number of
> arguments to being an implementation detail handled by the type of the argument class.  The reason
> is because function classes usually do some fairly heavy lifting which shouldn't be repeated for the
> N number of functions.

Ah, I see. No, this function class doesn't expose anything except the
ability to apply one of its members to an argument list. Trivial.

I'm not even sure I'm going to need need more than one class. There
are three plausible interfaces to each function: direct call (known
function, no need for a Function object), funcall (unknown function
applied to a known number of arguments), and apply (unknown function
applied to an unknown number of arguments).

It's trivial to do funcall in terms of apply, but it does mean
constructing and destructing the argument list. On the other hand,
providing three interfaces instead of two means extra complexity and
error recovery. So I may just go with a single Function class
providing only an apply interface.

Robert Fischer

unread,
May 10, 2009, 5:21:28 PM5/10/09
to jvm-la...@googlegroups.com
That works, too.

hlovatt

unread,
May 10, 2009, 7:21:16 PM5/10/09
to JVM Languages
This is the same issue as the Mac build of the MLVM had, see:

http://groups.google.com/group/jvm-languages/browse_frm/thread/47b4423aa99be008?hl=en

Is it some problem with the MLMV patches or with the OpenJDK?

John Rose

unread,
May 11, 2009, 9:14:55 PM5/11/09
to jvm-la...@googlegroups.com
On May 9, 2009, at 6:01 AM, Rémi Forax wrote:

> I seems there are some problem with b58 integration :
> - the public javadoc doesn't contains java.dyn package.
> http://download.java.net/jdk7/docs/api/

I made an error (in configuration management) in the makefile which
controls which APIs are officially visible. We're fixing this in b59.

-- John

Reply all
Reply to author
Forward
0 new messages