Project Amber
Rémi Forax
Devoxx FR - April 2017
Project Amber
Rémi Forax
Devoxx FR - April 2017
Slide 2
Slide 2 text
Me, myself and I
Father
– 3 kids
Assistant Prof
– University Paris East Marne la Vallée
Developer
– OpenJDK, ASM, Pro, etc
Expert for the JCP
– invokedynamic, lambda, module
Project Valhalla
OpenJDK project
Started July 2014
Two new Features
– Value types
– Generics over primitive and value types
Slide 6
Slide 6 text
What’s wrong with objects ?
Every objects have an header
– 64 bits (for hotspot) wasted for every objects
Arrays of objects are array of references
– Cache misses :(
Too many references
– Too much allocation → Slow the GC
Slide 7
Slide 7 text
Value types
Code like a class, works like an int
value class complex {
private final double re;
private final double im;
...
}
No identity
Must be immutable (avoid stack aliasing)
Slide 8
Slide 8 text
Generics of value types
class ArrayList implements List {
private E[] elements;
...
}
List list= new ArrayList();
Will work with primitive types too !
List list = new ArrayList();
Slide 9
Slide 9 text
Value types + Generics
Problems :(
Bytecode is not ready for that !
Specialization of the generic code at runtime ?
By the JIT !
Generics over objects still erased for compatibility
Wildcards / Boxing
List> vs List vs List
Slide 10
Slide 10 text
Project Panama
Java does not fear C anymore
– Make the JIT generates the JNI Stub
Need metadata to describe
– C structs layout in memory
– Calling convention
Access C heap safely through interfaces
Slide 11
Slide 11 text
Project Panama
The VM is already able to do restricted auto-
vectorisation of loop over arrays of primitives
Add API to access to SIMD/AVX ops
– Need to define 128/256/512 bits int/double ?
●
Use value types (defined by the JDK)
– Intrinsics for the VM
Slide 12
Slide 12 text
Current plan
Java 10
– panama
C without JNI + SIMD/AVX
– stealth valhalla: Value types in the VM
let JRuby, Groovy, Scala, etc play with it first
Java 11
– full valhalla: Value types + Specialized Generics
Slide 13
Slide 13 text
so no new language feature
in Java 10 ?
Slide 14
Slide 14 text
Amber !
Slide 15
Slide 15 text
Project Amber
http://openjdk.java.net/projects/amber/
explore and incubate
smaller, productivity-oriented language features
– Local-Variable Type Inference
– Enhanced Enums
– Lambda Leftovers
– ???
Slide 16
Slide 16 text
Local Variable Type Inference
simple
var a = 3;
var list = new ArrayList();
use super type:
var anonymous = new Object() {
int x;
};
not supported:
var lambda = x -> x + 2;
var ref = String::length;
Slide 17
Slide 17 text
Enhanced Enum
Generics in enum
enum ChronoField {
YEAR,
MONTH,
DAY_OF_MONTH,
DAY_OF_WEEK,
}
Frankenstenum in my opinion
Slide 18
Slide 18 text
Lambda leftovers
‘_’ means unused parameter
Map> map = ...
map.computeIfAbsent(key, _ - > new HashSet<>())
.add(value);
Lambda parameters open a new scope
map.merge(key, value, (old, value) - > old + value);
Better inference
– Fix cases where overloading is obvious for a human
Slide 19
Slide 19 text
Pattern Matching
Slide 20
Slide 20 text
No content
Slide 21
Slide 21 text
Brain Dump mode
Slide 22
Slide 22 text
Example
{ or [{
"username": "remi", "user": "remi",
... ...
} }, ...
v1 v2
Extracting a user name from
a JSON document
Slide 23
Slide 23 text
If instanceof …
{ or [{
"username": "remi", "user": "remi",
... ...
} }, ...
Object json = ...
if (json instanceof JSObject) {
return ((JSObject)json).getValue("username");
}
if (json instanceof JSArray) {
return ((JSArray)json).get(0).getValue("user");
}
...
Slide 24
Slide 24 text
Enhanced Switch
Switch on types
Object json = ...
switch(json) {
case JSObject:
return ((JSObject)json).getValue("username");
case JSArray:
return ((JSArray)json).get(0).getValue("user");
default: ...
}
Slide 25
Slide 25 text
Smart cast / type flow
Fuse instanceof/cast + declare a variable
Object json = ...
return switch(json) {
case JSObject object:
return
return object.getValue("username");
case JSArray array:
return array.get(0).getValue("user");
default: ...
};
Slide 26
Slide 26 text
Pattern Matching with Expression
“break” is so 1970 !
Object json = ...
return switch(json) {
case JSObject object ->
object.getValue("username")
case JSArray array ->
array.get(0).getValue("user")
default: ...
};
Slide 27
Slide 27 text
Default ?
We need a default because ...
Object json = ...
return switch(json) {
case JSObject object ->
object.getValue("username")
case JSArray array ->
array.get(0).getValue("user")
default: ...
};
Slide 28
Slide 28 text
Closed Hierarchy
Slide 29
Slide 29 text
Algebraic Data Type
Enumerate allowed subtypes
/* closed */ interface Expr {
data Value(int x)
data Add(Expr left, Expr right)
}
2 + (3 + 5)
new Add(new Value(2), new Add(…, …))
or
Add(Value(2), Add(Value(3), Value(5)))
Slide 30
Slide 30 text
Data Class
data Point(int x, int y);
Provide a way to represent ‘dumb’ data
– Abstraction ‘it’s a data class’
– Less boiler plate
Generate (at runtime ?)
– equals, hashCode, toString, getters ?
– Value types also need that too !
Slide 31
Slide 31 text
Destructured Pattern Matching
Data class fields can be extracted
public Expr simplify(Expr expr) {
return switch(expr) {
case Add(e1, e2) ->
...
case Value(value) ->
...
};
}
Look ‘ma, no default
Slide 32
Slide 32 text
Destructured Pattern Matching
Match part of the subtree
public Expr simplify(Expr expr) {
return switch(expr) {
case Add(Value(v1), Value(v2)) ->
...
case Add(e1, e2) ->
...
case Value(value) ->
...
};
}
Slide 33
Slide 33 text
Destructured Pattern Matching
With the implementations
public Expr simplify(Expr expr) {
return switch(expr) {
case Add(Value(v1), Value(v2)) ->
new Value(v1 + v2))
case Add(e1, e2) ->
simplify(...)
case Value value ->
value
};
}
Slide 34
Slide 34 text
‘_’ means unused/anything
Use _ in pattern
return switch(expr) {
case Add(Value(_), Value(v2)) ->
case Add(_, _) ->
case Value _ ->
};
Slide 35
Slide 35 text
Destructured Pattern Matching
You can not change a local variable !
public Expr simplify(Expr expr) {
return switch(expr) {
case Add(Value(v1), Value(v2)) ->
new Value(v1 + v2))
case Add(e1, e2) ->
simplify(...)
case Value _ ->
expr
};
}
Use of local variable (non final)
Slide 36
Slide 36 text
Encapsulation
Slide 37
Slide 37 text
Destructured vs Encapsulation
return switch(expr) {
case Add(Value(v1), Value(v2)) -> ...
...
};
if (expr instanceof Add) {
Add add = (Add)expr;
Expr left = add.getLeft();
if (left instanceof Value) {
Value v1 = (Value)left;
…
}
}
/* data */ class Add {
private final Expr left;
private final Expr right;
...
public Expr getLeft() {
return left;
}
public Expr getRight() {
return right;
}
}
Slide 38
Slide 38 text
De-constructor ?
return switch(expr) {
case Add(Value(v1), Value(v2)) -> ...
...
};
if (expr instanceof Add) {
Add add = (Add)expr;
Expr left, right = add.deconstructor();
if (left instanceof Value) {
Value v1 = (Value)left;
…
}
}
/* data */ class Add {
private final Expr left;
private final Expr right;
...
public Expr, Expr deconstructor() {
return left, right;
} ...
Slide 39
Slide 39 text
Under the hood
Slide 40
Slide 40 text
Implementation Troubles
Problems
– If instanceof … else
– Sharing
– De-constructor / Scala unapply
– Zero allocation
Goal
Pattern matching on types <=> as fast as method calls ?
Slide 41
Slide 41 text
If instanceof … else
instanceof == dynamic suite of typechecks
– Instanceof itself creates a series of if … else
Linearly checks all patterns is slooooooow
Slide 42
Slide 42 text
Use invokedynamic
The recipe is an array of pairs
Pattern / Action
●
A text transformed as an automata at runtime
●
Action: a static method to call if the pattern match
No instanceof
guardWithTest is a typecheck on runtime classes
Slide 43
Slide 43 text
And switch with statements ?
If limited to switch on types,
use the double switch trick !
String s;
int index = invokedynamic(expr);
recipe […]
switch(index) {
case 0:
s = "JSObject";
break;
case 1:
s = "JSArray";
break;
default:
s = "other";
}
return s;
Slide 44
Slide 44 text
Sharing
Some patterns starts with the same pattern
Several classes may match the same pattern
switch(expr) {
case IAdd(IValue(v1), IValue(v2)) -> ...
case IAdd(e1, e2) - > ...
IAdd
IValue IValue
extract
expr
v1 v2 action1
action2
Slide 45
Slide 45 text
At runtime (1/2)
switch(expr) {
case IAdd(IValue(v1), IValue(v2)) -> ...
case IAdd(e1, e2) - > ...
IAdd
IValue IValue
extract
expr
v1 v2 action1
action2
Add
Add Value
Value Value
Add extract
Add
v1
action2
Slide 46
Slide 46 text
At runtime (2/2)
switch(expr) {
case IAdd(IValue(v1), IValue(v2)) -> ...
case IAdd(e1, e2) - > ...
IAdd
IValue IValue
extract
expr
v1 v2 action1
action2
Add
Value Value
Add extract
Add
v1
action2
Value
v1
action1
Value
v2
Slide 47
Slide 47 text
Guard or map ?
switch(expr) {
case IAdd(IValue(v1), IValue(v2)) -> ...
case IAdd(e1, e2) - > …
At Runtime, we construct
Add extract
Add
v1
action2
Value
v1
action1
Value
v2
After N guards, use java.lang.ClassValue instead
Slide 48
Slide 48 text
De-constructor / unapply
Scala unapply is slow (Tuple and Optional are objects)
/* data */ class Add {
private final Expr left;
private final Expr right;
...
public Optional> unapply() {
return Some((left, right));
}
}
Extract the values => 2 object creations
Slide 49
Slide 49 text
Idea !
Create only one object for the whole pattern
expr == Add(Value(v1), Value(v2)) <=>
var foo = new $Foo$();
(expr as Add).deconstructor(foo::args1, foo::args2);
(foo.args1 as Value).deconstructor(foo:args3);
(foo args2 as Value).deconstructor(foo::args4);
action(args3, args4);
/* runtime */ class $Foo$ {
Expr arg1;
Expr arg2;
int arg3;
int arg4;
}
args1 args2 args3 args4
Slide 50
Slide 50 text
Magic !
Avoid the creation of the $Foo$ object,
with the help of the VM
Reify the stackframe as an object
var frame = StackFrame.alloc(LExpr;LExpr;II)
add.deconstructor(frame.refAt(0),
frame.refAt(1));
…
action.invokeWithStackFrame(frame, 2);
John Rose starts a prototype :)
Slide 51
Slide 51 text
Summary
Slide 52
Slide 52 text
Java 10 – Speculative planning
Panama
Stealh Valhalla
– Value types in the VM
Amber
– A lot of small improvements
– Pattern Matching
Slide 53
Slide 53 text
Q & A
Slide 54
Slide 54 text
Pizza into Java:
translating theory into practice
Martin Odersky, Philip Wadler
POPL 97