@drkrab Erjang: Erlang on the JVM Kresten Krab Thorup Trifork Thursday, May 12, 2011
Erjang: Goals • Learn Erlang • Discover “Actor Programming” • Meet great people Thursday, May 12, 2011
What is Erjang? • Erjang is an execution engine for BEAM byte code, written in Java. • JIT -Compiles BEAM to JVM byte code. • Runtime uses shared heap model. • BIFs and drivers are written in Java. Thursday, May 12, 2011
Your Erlang Program OTP Framework BEAM BEAM Emulator BIFs Your favorite OS Thursday, May 12, 2011
Your Erlang Program OTP Framework BEAM ERJANG BIFs JVM Java Virtual Machine Your favorite OS Thursday, May 12, 2011
Let’s see it in action... Thursday, May 12, 2011
Erlang ≠ Erjang Shared heap ¬ Native drivers ¬ NIF Thursday, May 12, 2011
Why Java? • JVM is everywhere (from mobile to AS/400) • JVM has many libraries / integrations. • JVM has 500+ man-years of engineering • JVM is fast (for Java-ish programs). • ... and I know JVM pretty well, ... Thursday, May 12, 2011
These are experimenting... StreamBase (CEP) Running Erlang-based FSMs on event streams ErlangIDE (Eclipse) Run compiler, type analysis, etc. Thursday, May 12, 2011
Erjang - Challenges • Tail-recursion • Interfacing to Java • Ultra Light-Weight Processes • Real-time Behavior • Arbitrary Precision Numbers • Erlang Drivers • JVM is safe, urgh! Thursday, May 12, 2011
JIT Compiler BEAM Reader ring.beam Type Analysis JVM Codegen ring-2a149ada.jar Thursday, May 12, 2011
* JIT Compiler error_handler:undefined_function erlang:load_module BEAM Reader ring.beam Type Analysis ClassLoader.loadClass(“erjang.m.ring”) JVM Codegen ring-2a149ada.jar Thursday, May 12, 2011
Erlang ⇒ JVM • Module ⇒ Class (+support in a “.jar”) • Function ⇒ Static Method + “EFun” object • Value ⇒ Object Instance ETuple, EPair, EFun, ESmall, EBig, ... Thursday, May 12, 2011
Erlang ⇒ JVM -module(bar). process([H | T], T2) -> process(T, foo(H, T2)); process([], T2) -> T2. foo(H, T) -> lists:reverse(H ++ T). Thursday, May 12, 2011
The BEAM Code {function, process, {nargs,2}}. {label,264}. {test,is_nonempty_list,{else,265},[{x,0}]}. {get_list,{x,0},{x,0},{y,0}}. {call,2,foo}. {move,{x,0},{x,1}}. {move,{y,0},{x,0}}. {call_last,2,process,1}. {label,265}. {test,is_nil,{else,263},[{x,0}]}. {move,{x,1},{x,0}}. return. {label,263}. {func_info,{atom,appmon_bar},{atom,process},2}. Thursday, May 12, 2011
public static EObject process___2(EProc eproc, EObject arg1, EObject arg2) { ECons cons; ENil nil; tail: if((cons = arg1.test_nonempty_list()) != null) { // extract list EObject hd = cons.head(); EObject tl = cons.tail(); // call foo/2 EObject tmp = foo___2$call(eproc, hd, arg2); // self-tail recursion arg1 = tl; arg2 = tmp; goto tail; } else if ((nil = arg1.test_nil()) != null) { return arg2; } throw ERT.func_info(am_bar, am_process, 2); } Thursday, May 12, 2011
DEMO • c(foo). • c(foo). • foo:main(). Thursday, May 12, 2011
Erlang ⇒ JVM -module(bar). process([H | T], T2) -> process(T, foo(H, T2)); process([], T2) -> T2. foo(H, T) -> lists:reverse(H ++ T). Thursday, May 12, 2011
foo(H, T) -> lists:reverse(H ++ T). public static EObject foo__2(EProc p, EObject H, EObject T) { EObject r = foo__2$body(p,H,T); while (r == TAIL_MARKER) { r = p.tail.go(); } return r; } Thursday, May 12, 2011
foo(H, T) -> lists:reverse(H ++ T). public static EObject foo__2$body(EProc p, EObject H, EObject T) { // Tmp = erlang:’++’(H,T) EObject tmp = erlang_append__2.invoke(p,H,T); // return lists:reverse(Tmp) p.tail = lists__reverse_1; p.arg1 = tmp; return TAIL_MARKER; } Thursday, May 12, 2011
foo(H, T) -> lists:reverse(H ++ T). package erjang.m.bar; class bar extends ECompiledModule { @Import(module=“lists”, fun=“reverse”, arity=1) static EFun1 lists__reverse__1 = null; @Import(module=“erlang”, fun=“++”, arity=2) static EFun2 erlang__append__2 = null; ... } Thursday, May 12, 2011
Interface to Java • Calling to JAVA from ERJANG • BIFs: special calling convention • Plain Java: Type conversions • Calling ERJANG from JAVA Thursday, May 12, 2011
Example BIF // foo:bar(...) native Function package erjang.m.foo; class foo extends ENative { @BIF public static EObject bar(EProc proc, EObject arg1, arg2, ...) { } } Thursday, May 12, 2011
Example BIF @BIF public static EObject spawn_link(EProc proc, EObject mod, EObject fun, EObject args) throws Pausable { EAtom m = mod.testAtom(); EAtom f = fun.testAtom(); ESeq a = args.testSeq(); if (m==null||f==null||a==null) throw ERT.badarg(mod, fun, args); EProc p2 = new EProc(proc.group_leader(), m, f, a); p2.link_to(proc); ERT.run(p2); return p2.self_handle(); } Thursday, May 12, 2011
demo() -> Map = 'java.util.HashMap':new(), Map:put('x', "4"), Map:put(1, 'foo'), print(Map). print([]) -> ok; print([{Key,Val}|Tail]) -> io:format("key=~p, value=~p~n", [Key,Val]), print(Tail). Thursday, May 12, 2011
� � new Thread() { � � � { � � � � setDaemon (true); � � � � start (); � � � } � � � public void run() { � � � � String [] ARGS = { � � � � � � "-progname" , "ej" , � � � � � � "-home" , System . getProperty ( "user.home" ), � � � � � � "-root" , "/Users/krab/Projects/OTP_R13B04" , � � � � � � "+A" , "2" , � � � � � � "+S" , "1" , � � � � � � "+e" , "5.7.5" , � � � � � � "-s" , "rpc" , "erjang_started" � � � � � }; � � � � try { � � � � � erjang . Main . main ( ARGS ); � � � � } catch ( Exception e ) { � � � � � e . printStackTrace (); � � � � } � � � }; � � }; Thursday, May 12, 2011
� � // � � // The -s rpc erjang_started argument // makes the loader call rpc:erjang_started/0 � � // which is the trigger that will release // 'wait_for_erjang_started' � � // � � System . err . println ( "did launch Erjang, ... waiting" ); � � RPC . wait_for_erjang_started ( 60 * 1000L ); � � � � // � � // Call erlang:display( ["Hello, Joe!~n", []] ) � � // � � EString hello_str = EString . fromString ( "Hello, Joe!~n" ); � � ESeq format_args = EList . make ( hello_str , ERT . NIL ); � � � � RPC . call ( am_erlang , am_display , ( EObject ) format_args ); Thursday, May 12, 2011
Light-Weight Processes • Threads don’t cut it. • Erjang uses Kilim , a JVM byte code rewriter • Allows scheduling processes onto threads. Thursday, May 12, 2011
Kilim ;":") ",,-&"&%. &'!%( /'&%( !"#$% $#) )*%)+ )-.% 345 *%"! 2$-1"&2-, /'&%( &#",$6-#0 %<&%#,"1 0-.%1 )*%)+ )-.% ",,-&"&2-,$ 7212089%":%# http://www.malhar.net/sriram/kilim/ Thursday, May 12, 2011
Kilim Rewriting int execute() throws Pausable { msg = mbox.get(); return msg.size(); } Thursday, May 12, 2011
Kilim Rewriting int execute() throws Pausable { throw KilimError(); } int execute(Fiber f) throws Pausable { msg = mbox.get(f); return msg.size(); } Thursday, May 12, 2011
Kilim Rewriting int execute(Fiber f) throws Pausable { f.down(); msg = mbox.get(f); switch(f.up()) { case PAUSING|NO_STATE: f.setState(new State( locals )); case PAUSING|HAS_STATE: return 0; case RUNNING|HAS_STATE: locals = f.getState(); case RUNNING|NO_STATE: } return msg.size(); } Thursday, May 12, 2011
Kilim Rewriting void execute(Fiber f) throws Pausable { switch (f.pc) { case 0: // default f.down(); mbox.get(f); switch(f.up()) { case PAUSING|NO_STATE: f.setState(new State( ... locals... )); case PAUSING|HAS_STATE: return 0; case NORMAL|HAS_STATE: locals = f.getState(); case NORMAL|NO_STATE: } case 1: // default return msg.size(); } } Thursday, May 12, 2011
The ring! Thursday, May 12, 2011
Thursday, May 12, 2011
Thursday, May 12, 2011
Test Setup erjang01.hosted.netic.dk • All run as JUnit tests (wrappers generated) • Comparison runs Module :test() Erjang vs BEAM • Running TRIQ tests comparison/local • Run Erlang test_server based tests (single- node mode) • Plain old JUnit tests Thursday, May 12, 2011
erl - boot time Thursday, May 12, 2011
erl - boot memory Thursday, May 12, 2011
EStones Thursday, May 12, 2011
@drkrab Java 8 • MethodHandle • Tail calls • Coroutines Would not be surprised to see Erjang 10x speedup Thursday, May 12, 2011
@drkrab Erjang • It’s real • It’s fast • ... still lots to do. Thursday, May 12, 2011
Thursday, May 12, 2011
Recommend
More recommend