Search |
||
The tales of the four Fibonacci'sPosted by forax on November 1, 2009 at 5:17 AM PST
Let me introduce a new language named pseudo (Why this name ?
Why another language ? Why God ? all these questions will be answered
in a later blog).
any is similar to java.lang.Object in Java, but don't require any narrow cast (casts are implicit) and any is also a super type of all primitive types. In fact, in pseudo, there is no distinction between reference types and primitive types. any is equivalent to C# 4.0 dynamic but it's implementation requires, I think, less boxing and less conversions that its C# counterpart. Status of the language
pseudo will run on the upcoming Java 7 platform, a Java platform
that enables JSR 292 i.e a Java platform with the new bytecode instruction invokedynamic.
def fib(n) {
if (n < 2) {
return n
}
return fib(n - 1) + fib(n - 2)
}
public static java.lang.Object fib(java.lang.Object) throws java.lang.Throwable;
Code:
0: aload_0
1: iconst_2
2: invokedynamic #2, 0 // NameAndType "__operator__:<":(Ljava/lang/Object;I)Z
7: ifeq 12
10: aload_0
11: areturn
12: aload_0
13: iconst_1
14: invokedynamic #3, 0 // NameAndType "__operator__:-":(Ljava/lang/Object;I)Ljava/lang/Object;
19: invokestatic #4 // Method fib:(Ljava/lang/Object;)Ljava/lang/Object;
22: aload_0
23: iconst_2
24: invokedynamic #3, 0 // NameAndType "__operator__:-":(Ljava/lang/Object;I)Ljava/lang/Object;
29: invokestatic #4 // Method fib:(Ljava/lang/Object;)Ljava/lang/Object;
32: invokedynamic #5, 0 // NameAndType "__operator__:+":(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
37: areturn
As you see, if you see something, most calls are translated into invokedynamic, but not all of them.
recursive call to fib are translated into invokestatic.
So even in a dynamic language, some part of the code are not dynamic at all.
As I said in the introduction, pseudo is a language that allows gradual typing,
You can, by example, indicates that the parameter of fib is an int.
def fib(int n) {
if (n < 2) {
return n
}
return fib(n - 1) + fib(n - 2)
}
public static java.lang.Object fib(int) throws java.lang.Throwable;
Code:
0: iload_0
1: iconst_2
2: if_icmpge 10
5: iload_0
6: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
9: areturn
10: iload_0
11: iconst_1
12: isub
13: invokestatic #3 // Method fib:(I)Ljava/lang/Object;
16: iload_0
17: iconst_2
18: isub
19: invokestatic #3 // Method fib:(I)Ljava/lang/Object;
22: invokedynamic #4, 0 // NameAndType "__operator__:+":(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
27: areturn
Wow, only the + between the two recursive calls of fib is dynamic.
Also not that n (local variable 0) at bytecode 6 is boxed to be an Integer.
def fib(n):int {
if (n < 2) {
return n
}
return fib(n - 1) + fib(n - 2)
}
public static int fib(java.lang.Object) throws java.lang.Throwable;
Code:
0: aload_0
1: iconst_2
2: invokedynamic #2, 0 // NameAndType "__operator__:<":(Ljava/lang/Object;I)Z
7: ifeq 17
10: aload_0
11: invokedynamic #3, 0 // NameAndType __cast__:(Ljava/lang/Object;)I
16: ireturn
17: aload_0
18: iconst_1
19: invokedynamic #4, 0 // NameAndType "__operator__:-":(Ljava/lang/Object;I)Ljava/lang/Object;
24: invokestatic #5 // Method fib:(Ljava/lang/Object;)I
27: aload_0
28: iconst_2
29: invokedynamic #4, 0 // NameAndType "__operator__:-":(Ljava/lang/Object;I)Ljava/lang/Object;
34: invokestatic #5 // Method fib:(Ljava/lang/Object;)I
37: iadd
38: ireturn
Compared to the full dynamic version, the only optimisation here is that the + between the two fib calls
can be computed using iadd instead of relying on a dynamic call.
Moreover, a dynamic cast was added to convert n as an int at instruction 11.
def fib(int n):int {
if (n < 2) {
return n
}
return fib(n - 1) + fib(n - 2)
}
public static int fib(int) throws java.lang.Throwable;
Code:
0: iload_0
1: iconst_2
2: if_icmpge 7
5: iload_0
6: ireturn
7: iload_0
8: iconst_1
9: isub
10: invokestatic #2 // Method fib:(I)I
13: iload_0
14: iconst_2
15: isub
16: invokestatic #2 // Method fib:(I)I
19: iadd
20: ireturn
The generated code is exactly the same that the one generated by javac for a static method fib written in Java. No dynamic call at all. Benchmark
No blog entry is really a good blog entry without a benchmark :)
result time
fib : 102334155 3,62 s
fib-param : 102334155 3,05 s
fib-return : 102334155 3,42 s
fib-typed : 102334155 1,52 s
First, using a dynamic language have a cost at runtime.
Using a partially typed language reduce the overhead a bit but
the cost of boxing/unboxing is still there.
Cheers, »
Related Topics >>
Blogs J2SE Java Specification Request Open JDK Performance Programming Virtual Machine Comments
Comments are listed in date ascending order (oldest first)
What happens at runtime?
Submitted by mernst on Fri, 2009-11-06 06:44.
Remi, cool stuff!
It would be very instructional if you could elaborate what happens at runtime, i.e. one-time/repeated dynamic binding, inline caches assuming "int" as receiver type, or whatever is going on there...
Matthias
Re: What happens at runtime?
Submitted by forax on Sun, 2009-11-15 16:25.
Hi Matthias,
The runtime relies on invokedynamic machinery, so the runtime specializes the calling code for each call site. At first call, the runtime gather parameters that are typed as any and try to find a corresponding implementation method using parameter classes. Then it installs runtime checks against parameter classes to call the same method if parameters classes are the same. So it's a kind of inlining cache. Rémi Based on this example one
Submitted by olefevre on Sun, 2009-11-15 12:32.
Based on this example one might be forgiven for thinking there is no point bothering with gradual typing since you had to go all the way to full typing to get any appreciable benefit. There are other arguments than performance for gradual typing, though.
Re: Based on this example
Submitted by forax on Sun, 2009-11-15 16:31.
Yes, you're right.
Detecting errors at compile time is the major benefit of gradual typing. Rémi |
||
|
|
Fools seldom differ....