Perf comparison, Re: Simple dynamic language using invokedynamic
Charles Oliver Nutter
headius at headius.com
Tue Jun 16 01:20:18 PDT 2009
On Mon, Jun 15, 2009 at 2:18 AM, Charles Oliver Nutter
<headius at headius.com> wrote:
> This is "Juby", a language that simply compiles Ruby's syntax to Java's
> type system. All calls are made using invokedynamic. Operators are also
> calls, and there's some special-cased logic in them to bootstrap
> appropriate utility methods for doing +, ==, etc. Other than primitive
> logics like "puts" compiling as a System.out.println, all calls are made
> through invokedyanamic. This makes the bytecode trivial to construct,
> and only requires a bit of work on the bootstrap side.
I've added enough logic to run "fib" in Juby and thought I'd get a
perf comparison. So here's the Juby source for fib(35):
def fib(a)
if a < 2
a
else
fib(a - 1) + fib(a - 2)
end
end
puts fib(35)
(this is essentially identical to the Ruby code)
And here's the Java code, with all the same fully-boxed logic, utility
math methods, and so on:
public class Fib {
public static void main(String[] args) {
System.out.println(fib(Long.valueOf(35)));
}
public static Long fib(Long a) {
if (lt(a, Long.valueOf(2))) {
return a;
} else {
return plus(fib(minus(a, Long.valueOf(1))), fib(minus(a,
Long.valueOf(2))));
}
}
public static Long plus(Long a, Long b) {
return a + b;
}
public static Long minus(Long a, Long b) {
return a - b;
}
public static Boolean lt(Long a, Long b) {
return a < b;
}
}
A comparison of bytecode generated in both cases...first Juby:
~/projects/juby ➔ javap -c fib
Compiled from "fib.jb"
public class fib extends java.lang.Object {
public static void main(java.lang.String[]);
Code:
0: aconst_null
1: pop
2: aconst_null
3: ldc2_w #48; //long 35l
6: invokestatic #16; //Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
9: invokedynamic #45, 0; //NameAndType
fib:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
14: getstatic #55; //Field java/lang/System.out:Ljava/io/PrintStream;
17: swap
18: invokevirtual #61; //Method
java/io/PrintStream.println:(Ljava/lang/Object;)V
21: aconst_null
22: pop
23: return
public static java.lang.Object fib(java.lang.Object);
Code:
0: aload_0
1: ldc2_w #9; //long 2l
4: invokestatic #16; //Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
7: invokedynamic #34, 0; //NameAndType
__lt__:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
12: getstatic #40; //Field java/lang/Boolean.FALSE:Ljava/lang/Boolean;
15: if_acmpeq 22
18: aload_0
19: goto 63
22: aconst_null
23: aload_0
24: ldc2_w #41; //long 1l
27: invokestatic #16; //Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
30: invokedynamic #44, 0; //NameAndType
"-":(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
35: invokedynamic #45, 0; //NameAndType
fib:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
40: aconst_null
41: aload_0
42: ldc2_w #9; //long 2l
45: invokestatic #16; //Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
48: invokedynamic #44, 0; //NameAndType
"-":(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
53: invokedynamic #45, 0; //NameAndType
fib:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
58: invokedynamic #47, 0; //NameAndType
"+":(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
63: areturn
static {};
Code:
0: ldc #19; //String fib
2: invokestatic #25; //Method
java/lang/Class.forName:(Ljava/lang/String;)Ljava/lang/Class;
5: invokestatic #31; //Method
com/headius/juby/SimpleJavaBootstrap.registerBootstrap:(Ljava/lang/Class;)V
8: return
}
And the Java code:
~/projects/juby ➔ javap -c Fib
Compiled from "Fib.java"
public class Fib extends java.lang.Object {
public Fib();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc2_w #3; //long 35l
6: invokestatic #5; //Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
9: invokestatic #6; //Method fib:(Ljava/lang/Long;)Ljava/lang/Long;
12: invokevirtual #7; //Method
java/io/PrintStream.println:(Ljava/lang/Object;)V
15: return
public static java.lang.Long fib(java.lang.Long);
Code:
0: aload_0
1: ldc2_w #8; //long 2l
4: invokestatic #5; //Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
7: invokestatic #10; //Method
lt:(Ljava/lang/Long;Ljava/lang/Long;)Ljava/lang/Boolean;
10: invokevirtual #11; //Method java/lang/Boolean.booleanValue:()Z
13: ifeq 18
16: aload_0
17: areturn
18: aload_0
19: lconst_1
20: invokestatic #5; //Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
23: invokestatic #12; //Method
minus:(Ljava/lang/Long;Ljava/lang/Long;)Ljava/lang/Long;
26: invokestatic #6; //Method fib:(Ljava/lang/Long;)Ljava/lang/Long;
29: aload_0
30: ldc2_w #8; //long 2l
33: invokestatic #5; //Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
36: invokestatic #12; //Method
minus:(Ljava/lang/Long;Ljava/lang/Long;)Ljava/lang/Long;
39: invokestatic #6; //Method fib:(Ljava/lang/Long;)Ljava/lang/Long;
42: invokestatic #13; //Method
plus:(Ljava/lang/Long;Ljava/lang/Long;)Ljava/lang/Long;
45: areturn
(utility methods omitted; they're basically identical to the ones in
Juby's one runtime class)
The logic is not a whole lot different. I've got a little extra
overhead in Juby because it inherits Ruby's "everything is an
expression" so I have a bit more bogus value pushing + popping, but
the resulting code is still pretty clean.
The benchmark is to simply run both of these, precompiled, with -Xint.
This doesn't show us what the optimized performance of each would be,
because indy doesn't jit or optimize right now. But it does show what
overhead we're paying for the indy + method handles at the moment.
Juby:
~/projects/juby ➔ time java -Xint -XX:+EnableInvokeDynamic -cp src:. fib
9227465
real 0m19.616s
user 0m19.307s
sys 0m0.104s
Java:
~/projects/juby ➔ time java -Xint Fib
9227465
real 0m17.857s
user 0m17.540s
sys 0m0.095s
This is very exciting to me. I'm looking forward to seeing the
jitted/optimized performance.
- Charlie
More information about the mlvm-dev
mailing list