AWS Community Day CPH - Three problems of Terraform
Message Passing Concurrency in Clojure using Kilim
1. Kilim-Clojure integration
Message Passing Concurrency in Clojure using
Kilim
Antonio Garrote
Forward
November 7, 2011
2. Kilim-Clojure integration
Clojure’s Concurrency Model
JVM thread basic autonomous work unit
Java shared memory explicit communication primitives
Immutable data structures + implicit coordination
mechanisms (STM, Refs)
3. Kilim-Clojure integration
Message Passing Communication
1 % T e r m i t e Scheme b e n c h m a r k s r i n g . e r l
2 m a k e r e l a y ( Next ) − >
3 receive
4 K when K > 0 − >
5 Next ! K − 1 ,
6 m a k e r e l a y ( Next ) ;
7 K− >
8 Next ! K
9 end .
10
11 l o o p (K , C u r r e n t , N) when N > 1 − >
12 l o o p (K , spawn ( r i n g , m a k e r e l a y , [ C u r r e n t ] ) ,N − 1 ) ;
13
14 l o o p (K , C u r r e n t , ) −>
15 s e l f ( ) ! K,
16 make relay ( Current ) .
17
18 % N = number p r o c e s s e s
19 % K = number o f m e s s a g e e x c h a n g e s
20 r i n g (N, K) − >
21 l o o p (K , s e l f ( ) , N ) .
4. Kilim-Clojure integration
Message Passing Communication
1
2 N
N = 100K , K = 0
518ms, 5µs process creation
3 K
N = 100K , K = 1M
759ms, 0.76µs local msg . send.
4 6
5
5. Kilim-Clojure integration
Message Passing Communication
How does Erlang do it?
User level processes executing recursive functions
VM preemptive scheduling
One heap/stack per process, no global heap
Tail call optimisation
More predictable GC behaviour
6. Kilim-Clojure integration
Efficient implementation of message passing concurrency in the
Java Virtual Machine?
7. Kilim-Clojure integration
Kilim
“Kilim is a message-passing framework for Java that provides
ultra-lightweight threads and facilities for fast, safe, zero-copy
messaging between these threads”
https://github.com/kilim/kilim
2006 Sriram Srinivasan, Opera Group Cambridge University
Current version 0.72
8. Kilim-Clojure integration
Kilim Tasks
Task
1 p u b l i c c l a s s ExampleTask e x t e n d s Task {
2 execute
3 p u b l i c void execute () throws Pausable {
4 doStuff ( ) ;
5 p u s a b l e S t u f f ( ) ; // t h r o w s P a u s a b l e Worker
6 moreStuff ( ) ; Thread
7 }
8
9 } notify
Scheduler
9. Kilim-Clojure integration
Kilim Weaving: Bytecode Transformation
Additional step after compilation
Transforms pausable methods
Creates a custom class to store the task state
Associates a Fiber object, tracks task’s stack
Transformed code returns immediately if the task is paused
Worker threads can resume the execution of paused stacks
unwinding the associated fiber stack
10. Kilim-Clojure integration
Kilim Weaving: Bytecode Transformation
1 p u b l i c void execute ( Fiber f i b e r ) throws Pausable {
2 s w i t c h ( f i b e r . pc ) {
3 c a s e 0 : g o t o START ;
4 c a s e 1 : g o t o PAUSABLE CALL ;
5 }
6 START :
7 doStuff ( ) ;
8 PAUSABLE CALL :
9 f i b e r . down ( ) ;
10 p u s a b l e S t u f f ( f i b e r ) ; // t h r o w s P a u s a b l e
11 f i b e r . up ( ) ;
12 switch ( f i b e r . status ) {
13 c a s e NOT PAUSING NO STATE :
14 g o t o RESUME ;
15 c a s e NOT PAUSING HAS STATE :
16 restoresState ( state );
17 g o t o RESUME ;
18 c a s e PAUSING NO STATE :
19 captureState ( state );
20 return ;
21 c a s e PAUSING HAS STATE :
22 return ;
23 }
24 RESUME :
25 moreStuff ( ) ;
26 }
13. Kilim-Clojure integration
Kilim-Clojure Integration (first attempt)
Modified version of Clojure’s compiler
Clojure’s functions executed as Kilim’s tasks
Weaving performed at run-time
https://github.com/antoniogarrote/clojure/tree/kilim
15. Kilim-Clojure integration
Introduction
Clojure
Compiler
Compiles bytecode
Java Class
Java instrumentation
Kilim Weaved Java
clj-kilim
Weaver Class
Bytecode Transformation Bytecode Transformation
Class
Loader
16. Kilim-Clojure integration
Kilim-Clojure Integration: API
1 ( d e f − p a u s a b l e y i e l d i n g − f n [ mbox ]
2 ( mbox−get−pausable mbox ) )
3
4
5 ( def−pausable task−fn1 [ ]
6 ...
7 ( c a l l − p a u s a b l e y i e l d i n g − f n ambox )
8 ...)
9
10 ( d e f task−fn2 ( p a u s a b l e [] ...))
11
12
13 ( t a s k − s t a r t task−fn1 )
14 ( t a s k − s t a r t task−fn2 )
19. Kilim-Clojure integration
Ring example again
1 ( d e f n ˆ { : p a u s a b l e t r u e } make−relay [ ˆ k i l i m . M a i l b o x s e l f
2 ˆ k i l i m . Mailbox next
3 ˆ k i l i m . Mailbox f i n a l ]
4 ( loop [ k ( r e c e i v e s e l f ) ]
5 ( i f (> k 0 )
6 ( do
7 ( ! next ( dec k ) )
8 ( recur ( . get s e l f )))
9 ( do
10 ( ! next ( dec k ) )
11 ( when ( n o t ( n i l ? f i n a l ) )
12 (! f i n a l true ))))))
13
14
15 ( d e f n make−loop [ n k ]
16 ( l e t [ˆ k i l i m . Mailbox f i r s t − m a i l b o x ( k i l i m . Mailbox . )
17 ˆ k i l i m . Mailbox final−mailbox ( k i l i m . Mailbox . ) ]
18 ( loop [ current first−mailbox
19 n n]
20 ( i f (> n 1 )
21 ( recur
22 ( spawn ( p a u s a b l e [ ˆ k i l i m . M a i l b o x s e l f ]
23 ( make−relay s e l f c u r r e n t n i l ) ) )
24 ( dec n ) )
25 ( do ( spawn ( p a u s a b l e [ ˆ k i l i m . M a i l b o x ]
26 ( make−relay f i r s t − m a i l b o x c u r r e n t f i n a l − m a i l b o x ) ) )
27 (! first−mailbox k)
28 ( . getb final−mailbox ) ) ) ) ) )
21. Kilim-Clojure integration
Conclusions
Efficient message passing possible in the JVM thanks to Kilim
Clojure can be a nice match for Kilim (immutable messages,
run-time weaving)
Benefits of asynchronous evented code with a nicer interface
Limitations: non-preemptive
Way to go?
Improve performance
Add distribution