2. Objectives of this Presentation Emphasize the Multicore Revolution and its impact on Programming Introduce the Actor Model Present Show Case using Scala Conclusions Page 2
3. Motivation: From Single Core to Hard Core Multi-Core processor architectures and GPUs offer scaling-up opportunities But how can we harness these new hardware architectures? Obviously, we need to adapt our applications Fortunately, Multi-Threading respectively Concurrent Programming comes to our rescue Page 3
4. Concurrency Concurrency and Performance When talking about improving the performance of software concurrency and multi-threadingare often considered as a promising technique for achieving this goal. Multi-threading can improve the“real” performance of a system on multi-processor platforms without incurring the overhead of multi-processing. “perceivable” performance of multi-user systems (even on a single-processor platform)
5. Concurrency Practice Unfortunately ... Developing multi-threaded systems that are faster than single-threaded systems is a surprisingly non-trivial task some multi-threaded systems “only”run slower than their single-threaded counterparts most multi-threaded systems do not run at all, due to deadlocks theyproduce
6. Architectural Perspective Concurrency is not merely a programmer‘s choice Instead, it covers all abstraction layers: Architecture: every system needs its customized concurrency architecture Design: we must address issues such as synchronization and coordination Implementation: we need to use the right features for the right purpose The more a Concurrency approach supports this, the better! Page 6
7. Every System needs its Concurrency Architecture Image processing systems such as rendering farms or modalities require Pipes & Filters Home-automation systems work best with Concurrent Reactors Process control is supported by Half Sync/Half Async Logging servers could use self-organizing Leaders/Followers Mobile agents may use the Blackboard Architecture for Information Exchange Page 7
8. Let‘s start with Object Oriented Concurrent Programming Objects provide services (via methods) ... and data (members) Static members resemble global state – beware of Singletons! In general, objects represent reactive (i.e., passive) entities of control They interoperate by method invocations or state manipulation User-managed threads leverage multiple cores but handling of threads and shared resources is left to the developer Shared State Thread Page 8 data data methods methods Thread
9. Challenges of (Object-Oriented) Distributed and Concurrent Programming A lot of pitfalls such as deadlocks and race condidions due to Burden of thread management Access to shared state Managing Locks which doesn‘t scale for large distributed systems with high numbers of objects Page 9 Shared State data data methods methods Danger Zone Thread Thread
10. Alternative Solution: Actors Actors have been introduced in the early 1970s by Hewitt for AI and Distribution: the Actor model is a mathematical model of concurrent computationthat treats "actors" as the universal primitives of concurrent digital computation: in response to a message that it receives, an actor can make local decisions, create more actors, send more messages, and determine how to respond to the next message received. [Hewitt, 73] Page 10 Available almost everywhere: Scala, Erlang, F#, Axum, Io, Clojure, Java, Akka, Smalltalk, Prolog, AAL (Asynchronous Agents Library in Visual Studio 2010)
11. Properties of Actors According to Gul Agha, an actor is an autonomous, interacting component of a distributed system a concurrency primitive not sharing resources with other actors It comprises: an immutable identity (name, virtual address) a mutable local state methods to manipulate local state a thread of control (not necessarily an OS thread) Page 11 Actor State thread method method Actors
12. Fundamentals of the Actor Model An actor may: process messages send messages change local state create new actors Page 12 Actor State thread method Interface method << create >> Actors message
13. Differences between Objects and Actors Objects encapsulate state and behavior Actors encapsulate state, behavior and execution Page 13 State State thread method method Interface Interface method method
14.
15.
16. Request/Reply Request/Reply semantics by exchanging messages Actor must know identity of sender to reply Call-by-Value Method invocation semantics: Incoming message contains arguments Outgoing message contains result Manual solution: use tokens to correlate messages Page 16 Actor 2 Actor 1 <<send>> <<send>> ASYNCHRONOUS COMPLETION TOKEN
17. Actors may implement the Active Object Pattern A client invokes a method on the proxy. The proxy returns a future to the client, and creates a method request which it passes to the scheduler. The scheduler inserts the method request into the activation list (not shown here). When the method request becomes runnable, the scheduler removes it from the activation list (not shown here) and executes it. The method request executes the method on the servant and writes results, if any, to the future. Clientsobtain the method’s results via the future. Client Proxy Scheduler Servant method Future MethodRequest insert dispatch call method write read Detour: Active Object Pattern
18. Futures If the sender needs to coordinate with result, futures represent a powerful concept A future is an object that asynchronously waits for (and receives) the reply message of another actor may occur explicitly or implicitly should involve configurable timeouts Page 18 Akka Example: val future = actor !!! Message // sometimes later: future.await // sometimes later: val result = future.get Note: In Akka you could also use awaitOne() or awaitAll() to wake up until one or all futures contain the awaited results
19. Possible Strategies in Message Handling Actor implementations can open up the strategy for handling messages BYO (Bring-Your-Own): let developer configure how messages are arranged within message queue FIFO, priority based, etc. let developer provide dispatching strategy for handling messages such as using thread pools, single threads, etc. let developers add communication protocols for remote actors Page 19 StrategyContext algorithm() strategy() Strategy dispatch() ConcreteStrategyA ConcreteStrategyB
20. Remote Actors Actors do not need to be constrained to one machine Instead, they may be distributed across different machines In this case, we need middleware mechanisms such as Network-wide identities Repositories for actor registration Client-managed versus server-managed actors (in terms of lifecycle) Proxies for transparently accessing remote actors Messaging interop protocols Page 20 State Node A Node B State thread thread method Interface method method Interface method
21. Handling Fault-Tolerance Erlang provides its own philosophy for fault-tolerance: let it crash Also used by some Actor libraries such as Akka Different models possible: One for One: If one actor crashes, only restart the crashed actor All for One: If one actor crashes stop the others too and restart actors One for One more suitable if actors are independent: otherwise use All for One Can be applied hierarchically, i.e. one supervisor might be supervised by another supervisor Page 21 Supervisor link Supervisor link
22. Sharing Data betweenActors Agents are not a good solution for applications that share data This holds especially when combining multiple actions In database management systems, transactions come to our rescue Same can be done in memory using STM STM canbeadded to actors to allowsafesharing of commondata Page 22
23. STM Idea: Separate object from identity If object is going to be modifiedby an actor, let reference (identity) refer to new object Objects themselves remainunchanged (immutability!) If anotheractor has changed the object in the meantime roll back change otherwise commit change Page 23 Ref Object val x Object‘
25. Example - Actors in Scala: Actor Definition Actors in Scala defined as library, but with an „internal DSL“ look & feel Actors must extend base class and define act() method Page 25 import scala.actors._ class Starship (val id : String) extends Actor { def act() { println("Engines of USS " + id + " starting") for (i <- 1 to 9) println("Warp " + i) println("Test succeeded") } } object ActorDemo1 extends Application { new Starship("Enterprise") start }
26. Example - Actors in Scala: Simplified Definition Using the actor method of the Actor companion object makes it even easier to define an actor: Page 26 import scala.actors._ import scala.actors.Actor._ object ActorDemo2 extends Application { val enterprise = actor { println("Engines of USS Enterprise starting") for (i <- 1 to 9) println("Warp " + i) println("Test succeeded") } }
27. Example - Actors in Scala: Processing Messages Messages are sent to an actor with the bang operator ! Messages are received within a receive block using pattern matching Page 27 case object STOP case object ACCELERATE object ActorDemo3 extends Application { val enterprise = actor { var continue = true; var speed = 0 while(continue) { receive { case msg : String => println("Received: " + msg) case STOP => println("stopping"); continue = false case ACCELERATE => speed += 1; println("New speed Warp " + speed) } } } enterprise ! "Hello, Spock" for (i <- 1 to 9) enterprise ! ACCELERATE enterprise ! STOP } [info] Running ActorDemo3 Received: Hello, Spock New speed is Warp 1 New speed is Warp 2 New speed is Warp 3 New speed is Warp 4 New speed is Warp 5 New speed is Warp 6 New speed is Warp 7 New speed is Warp 8 New speed is Warp 9 stopping [info] == run == [success] Successful.
28. Example - Actors in Scala: react instead of receive receive uses one thread and can return results reactmore efficient; does not return so that we need to use loop{while} Page 28 [info] Running ActorDemo4 Received: Hello, Spock New speed is Warp 1 New speed is Warp 2 New speed is Warp 3 New speed is Warp 4 New speed is Warp 5 New speed is Warp 6 New speed is Warp 7 New speed is Warp 8 New speed is Warp 9 stopping [info] == run == [success] Successful. case object STOP case object ACCELERATE class Starship (val id : String) extends Actor { private[this] var speed = 0 def act() { var continue = true loopWhile(true == continue) { react { case msg : String => println("Received: " + msg) case STOP => println("stopping"); continue = false case ACCELERATE => speed += 1; println("New speed is Warp " + speed) } } } } object ActorDemo4 extends Application { val enterprise = new Starship("Enterprise"); enterprise.start enterprise ! "Hello, Kirk“ for (i <- 1 to 9) enterprise ! ACCELERATE; enterprise ! STOP }
29. Example - Actors in Scala: react with reply In this example the actor returns a result using reply The sender receives the result synchronously using !? Page 29 case class AddItem (price: Double) case object SumUp case object CartDelete object Cart extends Actor { def act() { var total = 0.0 loop { react { case AddItem(price) => total += price case CartDelete => total = 0.0 case SumUp => reply(total); exit } } } } object CartDemo extends Application { Cart.start Cart ! AddItem(19.99); Cart ! AddItem(19.99); Cart ! AddItem(2.02) println("Please pay" + (Cart !? SumUp)) } [info] == run == [info] Running CartDemo Please pay 42.0 [info] == run == [success] Successful.
30. Example - Actors in Scala: react and receive with TIMEOUT If we use react and receive we might block infinitely without receiving a message (especially after not sending any messages anymore) Using the versions with with a predefined waiting time is more efficient in these circumstances On timeout a message TIMEOUT is sent to the actor Page 30 loopWhile(true == continue) { reactWithin(1000) { case msg : String => println("Received: " + msg) case STOP => println("stopping"); continue = false case ACCELERATE => speed += 1; println("New speed is Warp " + speed) } }
31. Example - Actors in Scala: Actors should not block using the ActiveActor pattern If actors block during processing a message, mutual deadlock conditions might occur. We can prevent this by separating message receipt and processing Page 31 object ActorDemo6 extends Application { val service = actor { def doWork() { // here the actual work is done val service = self actor { Thread.sleep(100) service ! "Serve" } } var served = 0 doWork() loop { // here messages are processed react { case "Serve" => println("I am serving"); served += 1 if (served < 3) doWork () case msg => println(msg) } } } service ! “Serve" }
32. Example - Actors in Scala: Using Schedulers Developers may override the scheduler method from the Actor trait For example, SingleThreadedScheduler maps the actor always to the same (main) thread Per default the ForkJoinScheduler is applied Page 32 class Starship (val id : String) extends Actor { override def scheduler = scala.actors.scheduler.SingleThreadedScheduler private[this] var speed = 0 def act() { var continue = true loopWhile(true == continue) { react { case msg : String => println("Received: " + msg) case STOP => println("stopping"); continue = false case ACCELERATE => speed += 1; println("New speed is Warp " + speed) } } } }
33. Example - Actors in Scala: Using Daemons Daemon actors provides the same interface such as actors, but The application may exit even if daemon actors are still running On the other hand, an application will wait for running actors to terminate In addition we might use lightweight reactors (do not transmit the sender with each message, may only use react) or ReplyReactors (senders are passed) Page 33 class Starship (val id : String) extends DaemonActor { private[this] var speed = 0 def act() { var continue = true loopWhile(true == continue) { react { case msg : String => println("Received: " + msg) case STOP => println("stopping"); continue = false case ACCELERATE => speed += 1; println("New speed is Warp " + speed) } } } }
34. Example - Actors in Scala: Back to the Future With Futures we can receive a reply from an actor asynchronously We need to use the !! operator which returns an object Using isSet on this object tells whether result is already available Page 34 case object STOP class Echo extends Actor { def act() { var continue = true while(continue) { receive { case msg : String => Thread.sleep(1000); sender ! msg case STOP => println("stopping"); continue = false } } } } object Future extends Application { val echo = new Echo; echo.start val replyMsg = echo !! "Hello OOP" println(replyMsg) println(replyMsg()) // get the result echo ! STOP } [info] == run == [info] Running Future <function0> Hello OOP stopping [info] == run == [success] Successful..
35. Example - Actors in Scala: Remote Actors Server Remote Actors can interact across machine or process boundaries The server needs to register itself Page 35 import scala.actors.Actor._ import scala.actors.remote.RemoteActor._ object Server { def remoteShip(ship : String, name: Symbol) = actor { alive(8888) register(name, self) var continue = true; var speed = 0 loopWhile(continue) { react { case"Stop" => println("Scotty is stopping"); continue = false case msg : String => println("Uhura received: " + msg) case i : Int => speed += i; println("Spock reports warp speed " + speed) } } } def main(args : Array[String]) { remoteShip("Enterprise", 'enterprise) } }
36. Example - Actors in Scala: Remote Actors Client The client connects to the remote node, ... ... selects an actor on this node, and sends messages to the remote actor Page 36 import scala.actors.Actor._ import scala.actors.remote._ import scala.actors.remote.RemoteActor._ object Client extends Application { val node = Node("127.0.0.1", 8888) actor { val ship = select(node, 'enterprise) ship ! "Hello" for (i <- 1 to 9) ship ! i ship ! "Stop" } } [info] Running Server Uhura received: Hello Spock reports warp speed 4 Spock reports warp speed 5 Spock reports warp speed 1 Spock reports warp speed 6 Spock reports warp speed 2 Spock reports warp speed 7 Spock reports warp speed 3 Spock reports warp speed 8 Spock reports warp speed 9 Scotty is stopping [info] == run == [success] Successfull.
37. Example - Actors in Scala: Concurrency Example (1) Example taken from Programming Scala by Venkat Subramaniam Task: find if number is perfect It is perfect its factors sum up to be twice the number Instead of using a single-threaded algorithm to check we are using multiple actors The number is partitioned into n ranges each of which an actor analyzes Kind of Map-Reduce / Master-Slave approach Page 37 .......... 1 to 1000 2001 to 3000 1001 to 2000
38. Example - Actors in Scala: Concurrency Example (2) Page 38 def sumOfFactors(number: Int) = { (0 /: (1 to number)) { (sum, i) => if (number % i == 0) sum + i else sum } } def isPerfect(candidate: Int) = { 2 * candidate == sumOfFactors(candidate) } def sumOfFactorsInRange(lower: Int, upper: Int, number: Int) = { (0 /: (lower to upper)) { (sum, i) => if (number % i == 0) sum + i else sum } } def isPerfectConcurrent(candidate: Int) = { val RANGE = 1000000 val numberOfPartitions = (candidate.toDouble / RANGE).ceil.toInt val caller = self for (i <- 0 until numberOfPartitions) { val lower = i * RANGE + 1; val upper = candidate min (i + 1) * RANGE actor { caller ! sumOfFactorsInRange(lower, upper, candidate) } } val sum = (0 /: (0 until numberOfPartitions)) { (partialSum, i) => receive { case sumInRange : Int => partialSum + sumInRange } } 2 * candidate == sum }
39. Actors to the Limits - Akka for Scala Akka provides a much more extensive Actor library developed by Jonas Bonér Offers typed and untyped actors Supports STM and Transactors Allows searching for actors in its registry Provides Oz-like Dataflow concurrency And more Erlang-like Fault Tolerance Serialization of actors and their references Add-on modules for OSGi, Camel, JTA, ... Page 39
40. Example - Actors in F# The functional programming language F# (standard language in Visual Studio 2010) also supports Actor-based programming Page 40 open System let mailbox = MailboxProcessor.Start(fun mb -> let rec loop x = async { let! msg = mb.Receive() printfn "I received: %s" msg return! loop x } loop 0) mailbox.Post("Hallo") mailbox.Post(“OOP“) Console.ReadLine() |> ignore
41. Example - Actors in Erlang Erlang - the first commercially relevant language that introduced actors (language feature) Scala inherited this approach Page 41 module_as_actor(E) when is_record(E, event) -> case lists:keysearch(mfa, 1, E#event.contents) of {value, {mfa, {M, F, _A}}} -> case lists:keysearch(pam_result, 1, E#event.contents) of {value, {pam_result, {M2, _F2, _A2}}} -> {true, E#event{label = F, from = M2, to = M}}; _ -> {true, E#event{label = F, from = M, to = M}} end; _ -> false end.
42. Alternative Approach: Using a DSL such as Microsoft Axum Page 42 Available for .NET Incubator project Actor definition in Axum, Code also in other .NET languages Domain Domain Shared State Shared State channel ChAddition { input int Add; output int Sum; Start: { Add -> S1; } S1: { Sum -> End; } } Reader Agent WriterAgent Agent:Reader WriterAgent Channel w/ protocol Agent:Reader domain A { intaccu; int sum(int op) { } agent Z : ChAddition{ } } Reader Agents WriterAgent WriterAgent WriterAgent Schema Schema Microsoft C++ developers should look at the AAL (Asynchronous Agents Library)
43. Different Actor Frameworks for Java available Akka ActorFoundry Actor‘s Guild Jetlang Kilim Page 43 Example from Akka Web site http://akkasource.org/ // server code class HelloWorldActor extends UntypedActor { public void onReceive(Object msg) { getContext().reply(msg + " World"); } } RemoteNode.start("localhost", 9999).register( "hello-service", actorOf(HelloWorldActor.class); // client code ActorRef actor = RemoteClient.actorFor( "hello-service", "localhost", 9999); Object res = actor.sendRequestReply("Hello");
44. Some Possible Liabilities of Actors* In the pure actor model there is no notion of behavior inheritance Dynamic creation of new actors leads to expansion of state necessity to decide about where to run and store a new actor maybe new communication channels Note: in a purely actor-based model entities such as integers would be actors Difficulty to replace behavior in an actor What about stream-based media? Fortunately, most of these liabilities can be avoided in hybrid OO/Actor models Page 44 *see http://www.doc.ic.ac.uk/~nd/surprise_97/journal/vol2/pjm2/
45. Summary Multi-core revolution is pushing developers hard; using self-managed threads and synchronization leads to accidental complexity The Actor model seems to be a promising alternative for Concurrent Programming New languages and frameworks mix functional, object-oriented concepts with actors Actors are not the right solution for all problems, e.g., for really stateful tasks Introduction of actors should follow design of software architecture, especially its concurrency and distribution architecture. Still valid: A fool with a tool is still a fool Page 45