Actor Concurrency
                         Alex Miller

Blog:   http://tech.puredanger.com     Twitter: @puredanger
8086
Moore’s Law?




http://en.wikipedia.org/wiki/CPU_speed   http://en.wikipedia.org/wiki/Transistor_count
Moore’s Law?




http://en.wikipedia.org/wiki/CPU_speed   http://en.wikipedia.org/wiki/Transistor_count
Moore’s Law?




http://en.wikipedia.org/wiki/CPU_speed   http://en.wikipedia.org/wiki/Transistor_count
State of the Practice
public class Counter {
  private int count = 0;

    public int increment() {
      synchronized(thi...
JCIP says:
•   It’s the mutable state, stupid.
•   Make fields final unless they need to be mutable.
•   Immutable objects a...
JCIP says:
•   It’s the mutable state, stupid.
•   Make fields final unless they need to be mutable.
•   Immutable objects a...
The problem with shared state concurrency
The problem with shared state concurrency




          ...is the shared state.
Actors

•   No shared state
•   Lightweight processes
•   Asynchronous message-passing
•   Buffer messages in a mailbox
• ...
The Basics
The Basics




             spawn
The Basics

send




                    spawn
The Basics

         receive
Erlang


• Invented at Ericsson
• Functional language
• Dynamic typing
• Designed for concurrency, fault-tolerance
Single assignment
Eshell V5.6.3 (abort with ^G)
1> A = 4.
4
2> A.
4
3> A = 6.
** exception error: no match of right hand
s...
Atoms and Tuples
1> F=foo.
foo
2> Stooges = {larry,curly,moe}.
{larry,curly,moe}
3> Me = {person,quot;Alexquot;,quot;nacho...
Lists
1> List=[1,2,3].
[1,2,3]
2> [First|Rest]=List.
[1,2,3]
3> First.
1
4> Rest.
[2,3]
5> List2=[4|List].
[4,1,2,3]
6> [C...
Functions
1> Square = fun(X) -> X*X end.
#Fun<erl_eval.6.13229925>
2> Square(5).
25.

3> TimesN = fun(N) -> (fun(X) -> N*X...
Modules
-module(mymath).
-export([square/1,fib/1]).

square(Value) -> Value*Value.

fib(0) -> 0;
fib(1) -> 1;
fib(N) when ...
Modules and Functions
-module(mymath2).
-export([fibtr/1]).

fibtr(N) -> fibtr_iter(N, 0, 1).

fibtr_iter(0, Result, _Next...
Concurrency Primitives

• Pid = spawn(fun)
• Pid ! message
• receive...end
Concurrency Primitives

• Pid = spawn(fun)
• Pid ! message
• receive...end      Pid
Concurrency Primitives

• Pid = spawn(fun)
• Pid ! message
• receive...end      Pid
Concurrency Primitives

• Pid = spawn(fun)
• Pid ! message
• receive...end      Pid
Concurrency Primitives

• Pid = spawn(fun)
• Pid ! message
• receive...end
                           receive
            ...
A Simple Process
loop() ->
  receive
    {toF, C} ->
       io:format(quot;~p C is ~p F~nquot;,
                 [C, 32+C*...
Spawn!
1> c(temperature).
{ok,temperature}
2> Pid = spawn(fun temperature:loop/0).
<0.128.0>
3> Pid ! {toC, 32}.
32F is 0....
Responding
-module(temp2).
-export([start/0,convert/2]).

start() ->
  spawn(fun() -> loop() end).

convert(Pid, Request) ...
Responding
loop() ->
  receive
    {From, {toF, C}} ->
       From ! {self(), 32+C*9/5},
       loop();
    {From, {toC, F...
Responding
1> c(temp2).
{ok,temp2}
2> Pid = temp2:start().
<0.57.0>
3> temp2:convert(Pid2, {toF, 100}).
212.0
Process Ring
Running Rings
1> c(ring).
2> T=ring:startTimer().
3> R=ring:startRing(100,T).
spawned 100 in 241 microseconds
4> R ! start...
Processes and
         Messages

• Processes cheap to create (10ks < second)
• Create many processes (10ks to 100ks)
• Mes...
Error Handling
Error Handling

     link
Error Handling

     link
Error Handling


    exit signal
Linked Processes
reportExit(WatchedPid) ->
  spawn(fun() ->
    process_flag(trap_exit, true),
    link(WatchedPid),
    r...
Linked Processes
1> c(link).
2> P1 = link:startWatched().
3> P2 = link:startWatched().
4> link:reportExit(P1).
5> link:rep...
Does it work?
Ericsson AXD301
• Telecom ATM switch
• Millions of calls / week
• 99.9999999% uptime =
  32 ms down / year
• -> Nortel Alt...
YAWS




http://www.sics.se/~joe/apachevsyaws.html
Messaging
Facebook Chat
“For Facebook Chat, we rolled our own subsystem
for logging chat messages (in C++) as well as an epoll-
driv...
And others...
• JVM-based language
• Object-functional hybrid
• Actor library
Scala Rings
def act() {
  loop {
    react {
      case StartMessage => {
         log(quot;Starting messagesquot;)
      ...
Scala Rings
    case TokenMessage(id,value) if id == nodeId => {
      val nextValue = value+1
      if(nextValue % 10000 ...
Comparison to Erlang

• receive-based actors cheap to create
  (Erlang about 3x faster)
• Can also create many processes
•...
•   Java-based framework with compile-time weaving

•   Tasks - lightweight threads/processes/etc

•   @pausable - let tas...
Kilim Ring
import kilim.Mailbox;
import kilim.Task;
import kilim.pausable;

public class NodeActor extends Task {
  privat...
Kilim Ring
@pausable
public void execute() throws Exception {
  while(true) {
    Message message = inbox.get();
    if(me...
Kilim Ring
    } else if(message instanceof TokenMessage) {
      TokenMessage token = (TokenMessage)message;
      if(tok...
Performance
               10.0                                             400
                7.5                       ...
http://tech.puredanger.com/presentations/actor-concurrency
Actor Concurrency
Actor Concurrency
Actor Concurrency
Actor Concurrency
Actor Concurrency
Actor Concurrency
Upcoming SlideShare
Loading in …5
×

Actor Concurrency

5,220 views

Published on

An introduction to Erlang and the actor model of concurrency.

Author: Alex Miller
Blog: http://tech.puredanger.com
Twitter: @puredanger

Published in: Technology
0 Comments
15 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
5,220
On SlideShare
0
From Embeds
0
Number of Embeds
70
Actions
Shares
0
Downloads
178
Comments
0
Likes
15
Embeds 0
No embeds

No notes for slide

Actor Concurrency

  1. 1. Actor Concurrency Alex Miller Blog: http://tech.puredanger.com Twitter: @puredanger
  2. 2. 8086
  3. 3. Moore’s Law? http://en.wikipedia.org/wiki/CPU_speed http://en.wikipedia.org/wiki/Transistor_count
  4. 4. Moore’s Law? http://en.wikipedia.org/wiki/CPU_speed http://en.wikipedia.org/wiki/Transistor_count
  5. 5. Moore’s Law? http://en.wikipedia.org/wiki/CPU_speed http://en.wikipedia.org/wiki/Transistor_count
  6. 6. State of the Practice public class Counter { private int count = 0; public int increment() { synchronized(this) { return ++count; } } }
  7. 7. JCIP says: • It’s the mutable state, stupid. • Make fields final unless they need to be mutable. • Immutable objects are automatically thread-safe. • Encapsulation makes it practical to manage the complexity. • Guard each mutable variable with a lock. • Guard all variables in an invariant with the same lock. • Hold locks for the duration of compound actions. • A program that accesses a mutable variable from multiple threads without synchronization is a broken program. • Don’t rely on clever reasoning about why you don’t need to synchronize. • Include thread safety in the design process - or explicitly document that your class is not thread-safe. • Document your synchronization policy.
  8. 8. JCIP says: • It’s the mutable state, stupid. • Make fields final unless they need to be mutable. • Immutable objects are automatically thread-safe. • Encapsulation makes it practical to manage the complexity. • Guard each mutable variable with a lock. • Guard all variables in an invariant with the same lock. • Hold locks for the duration of compound actions. • A program that accesses a mutable variable from multiple threads without synchronization is a broken program. • Don’t rely on clever reasoning about why you don’t need to synchronize. • Include thread safety in the design process - or explicitly document that your class is not thread-safe. • Document your synchronization policy.
  9. 9. The problem with shared state concurrency
  10. 10. The problem with shared state concurrency ...is the shared state.
  11. 11. Actors • No shared state • Lightweight processes • Asynchronous message-passing • Buffer messages in a mailbox • Receive messages with pattern matching
  12. 12. The Basics
  13. 13. The Basics spawn
  14. 14. The Basics send spawn
  15. 15. The Basics receive
  16. 16. Erlang • Invented at Ericsson • Functional language • Dynamic typing • Designed for concurrency, fault-tolerance
  17. 17. Single assignment Eshell V5.6.3 (abort with ^G) 1> A = 4. 4 2> A. 4 3> A = 6. ** exception error: no match of right hand side value 6 4> A = 2*2. 4
  18. 18. Atoms and Tuples 1> F=foo. foo 2> Stooges = {larry,curly,moe}. {larry,curly,moe} 3> Me = {person,quot;Alexquot;,quot;nachosquot;}. {person,quot;Alexquot;,quot;nachosquot;} 4> {person,Name,Food} = Me. {person,quot;Alexquot;,quot;nachosquot;} 5> Name. quot;Alexquot; 6> Food. quot;nachosquot;
  19. 19. Lists 1> List=[1,2,3]. [1,2,3] 2> [First|Rest]=List. [1,2,3] 3> First. 1 4> Rest. [2,3] 5> List2=[4|List]. [4,1,2,3] 6> [Char|String] = “abc”. “abc” 7> Char. 97
  20. 20. Functions 1> Square = fun(X) -> X*X end. #Fun<erl_eval.6.13229925> 2> Square(5). 25. 3> TimesN = fun(N) -> (fun(X) -> N*X end) 3> end. #Fun<erl_eval.6.13229925> 4> lists:map(TimesN(10), [1,2,3]). [10,20,30]
  21. 21. Modules -module(mymath). -export([square/1,fib/1]). square(Value) -> Value*Value. fib(0) -> 0; fib(1) -> 1; fib(N) when N>0 -> fib(N-1) + fib(N-2). 1> c(mymath.erl). 2> mymath:square(5). 25 3> mymath:fib(7). 13
  22. 22. Modules and Functions -module(mymath2). -export([fibtr/1]). fibtr(N) -> fibtr_iter(N, 0, 1). fibtr_iter(0, Result, _Next) -> Result; fibtr_iter(Iter, Result, Next) when Iter > 0 -> fibtr_iter(Iter-1, Next, Result+Next). 1> c(mymath2.erl). 2> mymath2:fibtr(200). 280571172992510140037611932413038677189525
  23. 23. Concurrency Primitives • Pid = spawn(fun) • Pid ! message • receive...end
  24. 24. Concurrency Primitives • Pid = spawn(fun) • Pid ! message • receive...end Pid
  25. 25. Concurrency Primitives • Pid = spawn(fun) • Pid ! message • receive...end Pid
  26. 26. Concurrency Primitives • Pid = spawn(fun) • Pid ! message • receive...end Pid
  27. 27. Concurrency Primitives • Pid = spawn(fun) • Pid ! message • receive...end receive Pid ... end
  28. 28. A Simple Process loop() -> receive {toF, C} -> io:format(quot;~p C is ~p F~nquot;, [C, 32+C*9/5]), loop(); {toC, F} -> io:format(quot;~p F is ~p C~nquot;, [F, (F-32)*5/9]), loop(); {stop} -> io:format(quot;Stopping~nquot;); Other -> io:format(quot;Unknown: ~p~nquot;, [Other]), loop() end.
  29. 29. Spawn! 1> c(temperature). {ok,temperature} 2> Pid = spawn(fun temperature:loop/0). <0.128.0> 3> Pid ! {toC, 32}. 32F is 0.0 C {convertToC,32} 4> Pid ! {toF, 100}. 100C is 212.0 F {convertToF,100} 5> Pid ! {stop}. Stopping {stop}
  30. 30. Responding -module(temp2). -export([start/0,convert/2]). start() -> spawn(fun() -> loop() end). convert(Pid, Request) -> Pid ! {self(), Request}, receive {Pid, Response} -> Response end.
  31. 31. Responding loop() -> receive {From, {toF, C}} -> From ! {self(), 32+C*9/5}, loop(); {From, {toC, F}} -> From ! {self(), (F-32)*5/9}, loop(); {From, stop} -> From ! {self(), stop}, io:format(quot;Stopping~nquot;); {From, Other} -> From ! {self(), {error, Other}}, loop() end.
  32. 32. Responding 1> c(temp2). {ok,temp2} 2> Pid = temp2:start(). <0.57.0> 3> temp2:convert(Pid2, {toF, 100}). 212.0
  33. 33. Process Ring
  34. 34. Running Rings 1> c(ring). 2> T=ring:startTimer(). 3> R=ring:startRing(100,T). spawned 100 in 241 microseconds 4> R ! start. {1230,863064,116530} Starting message {1230,863064,879862} Around ring 10000 times {1230,863065,642097} Around ring 20000 times ...etc {1230,863140,707023} Around ring 990000 times {1230,863141,471193} Around ring 1000000 times Start={1230,863064,116875} Stop={1230,863141,471380} Elapsed=77354505 5> R20k = ring:startRing(20000,T). spawned: 20000 in 118580 microseconds
  35. 35. Processes and Messages • Processes cheap to create (10ks < second) • Create many processes (10ks to 100ks) • Messages very fast to send (1M / second)
  36. 36. Error Handling
  37. 37. Error Handling link
  38. 38. Error Handling link
  39. 39. Error Handling exit signal
  40. 40. Linked Processes reportExit(WatchedPid) -> spawn(fun() -> process_flag(trap_exit, true), link(WatchedPid), receive {'EXIT', _Pid, Msg} -> io:format(quot;got linked exit: ~p~nquot;, [Msg]) end end). startWatched() -> spawn(fun() -> receive die -> exit(quot;diequot;); crash -> erlang:error(quot;crashquot;) end end).
  41. 41. Linked Processes 1> c(link). 2> P1 = link:startWatched(). 3> P2 = link:startWatched(). 4> link:reportExit(P1). 5> link:reportExit(P2). 6> P1 ! die. got linked exit: quot;diequot; 7> P2 ! crash. got linked exit: {quot;crashquot;,[{link,'- startWatched/0-fun-0-',0}]}
  42. 42. Does it work?
  43. 43. Ericsson AXD301 • Telecom ATM switch • Millions of calls / week • 99.9999999% uptime = 32 ms down / year • -> Nortel Alteon SSL Accelerator
  44. 44. YAWS http://www.sics.se/~joe/apachevsyaws.html
  45. 45. Messaging
  46. 46. Facebook Chat “For Facebook Chat, we rolled our own subsystem for logging chat messages (in C++) as well as an epoll- driven web server (in Erlang) that holds online users' conversations in-memory and serves the long-polled HTTP requests. Both subsystems are clustered and partitioned for reliability and efficient failover. ” Reference: http://www.facebook.com/note.php?note_id=14218138919
  47. 47. And others...
  48. 48. • JVM-based language • Object-functional hybrid • Actor library
  49. 49. Scala Rings def act() { loop { react { case StartMessage => { log(quot;Starting messagesquot;) timer ! StartMessage nextNode ! TokenMessage(nodeId, 0) } case StopMessage => { log(quot;Stoppingquot;) nextNode ! StopMessage exit }
  50. 50. Scala Rings case TokenMessage(id,value) if id == nodeId => { val nextValue = value+1 if(nextValue % 10000 == 0) log(quot;Around ring quot; + nextValue + quot; timesquot;) if(nextValue == 1000000) { timer ! StopMessage timer ! CancelMessage nextNode ! StopMessage exit } else { nextNode ! TokenMessage(id, nextValue) } } case TokenMessage(id,value) => { nextNode ! TokenMessage(id,value) } }
  51. 51. Comparison to Erlang • receive-based actors cheap to create (Erlang about 3x faster) • Can also create many processes • Messages also fast to send (Erlang about 2x faster)
  52. 52. • Java-based framework with compile-time weaving • Tasks - lightweight threads/processes/etc • @pausable - let task be paused during execution • sleep / yield • Mailbox (typed via generics) - single consumer • Messaging - mutable (in limited ways)
  53. 53. Kilim Ring import kilim.Mailbox; import kilim.Task; import kilim.pausable; public class NodeActor extends Task { private final int nodeId; private final Mailbox<Message> inbox; private final Mailbox<Message> timerInbox; private Mailbox<Message> nextInbox; public NodeActor(int nodeId, Mailbox<Message> inbox, Mailbox<Message> timerInbox) { this.nodeId = nodeId; this.inbox = inbox; this.timerInbox = timerInbox; }
  54. 54. Kilim Ring @pausable public void execute() throws Exception { while(true) { Message message = inbox.get(); if(message.equals(Message.START)) { timerInbox.putnb(Message.START); nextInbox.putnb(new TokenMessage(nodeId, 0)); } else if(message.equals(Message.STOP)) { nextInbox.putnb(Message.STOP); break; ...
  55. 55. Kilim Ring } else if(message instanceof TokenMessage) { TokenMessage token = (TokenMessage)message; if(token.source == nodeId) { int nextVal = token.value+1; if(nextVal == 1000000) { timerInbox.putnb(Message.STOP); timerInbox.putnb(Message.CANCEL); nextInbox.putnb(Message.STOP); break; } else { token.value = nextVal; nextInbox.putnb(token); } } else { nextInbox.putnb(token); } } }
  56. 56. Performance 10.0 400 7.5 300 milliseconds milliseconds 5.0 200 2.5 100 0 0 100 nodes 20k nodes 130,000 Erlang 97,500 milliseconds Scala Kilim 65,000 32,500 0 100M messages
  57. 57. http://tech.puredanger.com/presentations/actor-concurrency

×