From f98d062feee18b991b5a34777f4fa27650253829 Mon Sep 17 00:00:00 2001 From: Bryan Chan Date: Fri, 3 Mar 2017 00:38:40 -0500 Subject: [PATCH] Rename "Ring" to "BusyRing", and "Ring2" to "Ring" (Akka only at this time); also modify the latter to match its description in the literature. --- Akka/README.md | 44 ++++++------------ Akka/src/main/scala/BusyRing.scala | 53 ++++++++++++++++++++++ Akka/src/main/scala/Ring.scala | 58 ++++++++---------------- Akka/src/main/scala/Ring2.scala | 71 ------------------------------ README.md | 58 +++++++++++++++++++++++- 5 files changed, 142 insertions(+), 142 deletions(-) create mode 100644 Akka/src/main/scala/BusyRing.scala delete mode 100644 Akka/src/main/scala/Ring2.scala diff --git a/Akka/README.md b/Akka/README.md index 1d29141..b719160 100644 --- a/Akka/README.md +++ b/Akka/README.md @@ -1,35 +1,19 @@ -# Setup # +# Setup -Please install [sbt]("http://www.scala-sbt.org/0.13/docs/Setup.html"), [scala]("http://www.scala-lang.org/download/install.html"). +Install [sbt]("http://www.scala-sbt.org/0.13/docs/Setup.html") and [scala]("http://www.scala-lang.org/download/install.html"). On Ubuntu, this can be done with the following commands: -# Benchmarks # + echo "deb https://dl.bintray.com/sbt/debian /" | sudo tee -a /etc/apt/sources.list.d/sbt.list + sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 2EE0EA64E40A89B84B2DF73499E82A75642AC823 + sudo apt-get update + sudo apt-get install sbt scala -There are 7 benchmark programs. Descriptions of them could be found in the [Theater benchmark suite](Theater/README.md) +# Usage -To run Ring: + sbt "run-main Ring " + sbt "run-main BusyRing " + sbt "run-main Fork " + sbt "run-main TreeMsg " + sbt "run-main Pipeline " + sbt "run-main Chameneos " + sbt "run-main Calculator " - sbt "run-main Ring " - -To run Ring2: - - sbt "run-main Ring " - -To run Fork: - - sbt "run-main Fork " - -To run TreeMsg: - - sbt "run-main TreeMsg " - -To run Pipeline: - - sbt "run-main " - -To run Chameneos: - - sbt "run-main " - -To run Calculator: - - sbt "run-main " diff --git a/Akka/src/main/scala/BusyRing.scala b/Akka/src/main/scala/BusyRing.scala new file mode 100644 index 0000000..3e17c4c --- /dev/null +++ b/Akka/src/main/scala/BusyRing.scala @@ -0,0 +1,53 @@ +import java.util.Calendar +import akka.actor._ + +object BusyRing { + val system = ActorSystem("BusyRing") + var startTime: Long = 0 + var endTime: Long = 0 + + def main(args: Array[String]): Unit = { + val numNodes = args(0).toInt + val numMessages = args(1).toInt + val nodes = + for (i <- 0 until numNodes) yield system.actorOf(Props(classOf[RingNode], i, numMessages), "Node" + i) + for (i <- 0 until numNodes) nodes(i) ! Connect(nodes((i + 1) % numNodes)) + nodes(0) ! Start + } + + case object Start + case object Stop + case class Connect(next: ActorRef) + case class Token(goal: Int) + + class RingNode(val nodeId: Int, val numMessages: Int) extends Actor { + var nextNode: ActorRef = context.system.deadLetters + var completed: Int = 0 + + def receive = { + case Connect(next: ActorRef) => + // println(s"Actor $nodeId is connecting to ${next.path}") + nextNode = next + + case Start => + println("Start: \t" + Calendar.getInstance().getTime) + BusyRing.startTime = System.currentTimeMillis() + 1 to numMessages foreach { _ => + nextNode ! Token(nodeId) + } + + case Token(goal) => + if (goal == nodeId) { + completed += 1 + if (completed == numMessages) { + BusyRing.endTime = System.currentTimeMillis() + println("Stop: \t" + Calendar.getInstance().getTime) + println(s"Elapsed time: ${(BusyRing.endTime - BusyRing.startTime) / 1000.0}s") + system.shutdown() + } + } else { + nextNode ! Token(goal) + } + } + } +} diff --git a/Akka/src/main/scala/Ring.scala b/Akka/src/main/scala/Ring.scala index f710b3b..f8a90c0 100644 --- a/Akka/src/main/scala/Ring.scala +++ b/Akka/src/main/scala/Ring.scala @@ -1,71 +1,51 @@ import java.util.Calendar +import akka.actor._ -import akka.actor.{Actor, ActorRef, ActorSystem, Props} - -/** - * @author chupanw - */ object Ring { - - val system = ActorSystem() - + val system = ActorSystem("Ring") var startTime: Long = 0 var endTime: Long = 0 def main(args: Array[String]): Unit = { val numNodes = args(0).toInt val numRounds = args(1).toInt - run(numNodes = numNodes, numRounds = numRounds) - } - - def run(numNodes: Int, numRounds: Int): Unit = { - val nodes = spawnNodes(numNodes, numRounds) - nodes(0) ! Start - } - - def spawnNodes(numNodes: Int, numRounds: Int): Array[ActorRef] = { val nodes = - for (i <- 0 until numNodes) yield system.actorOf(Props(classOf[NodeActor], i, numRounds), "Node" + i) + for (i <- 0 until numNodes) yield system.actorOf(Props(classOf[RingNode], i, numRounds), "Node" + i) for (i <- 0 until numNodes) nodes(i) ! Connect(nodes((i + 1) % numNodes)) - nodes.toArray + nodes(0) ! Start } - // Messages case object Start case object Stop case class Connect(next: ActorRef) - case class Token(id: Int) - - class NodeActor(val nodeId: Int, val numRounds: Int) extends Actor { + case class Token(goal: Int, lapsRemaining: Int) + class RingNode(val nodeId: Int, val numRounds: Int) extends Actor { var nextNode: ActorRef = context.system.deadLetters - var returnCount: Int = 0 def receive = { - case Connect(next: ActorRef) => -// println(s"Actor $nodeId is connecting to ${next.path}") + // println(s"Actor $nodeId is connecting to ${next.path}") nextNode = next case Start => - startTime = System.currentTimeMillis() println("Start: \t" + Calendar.getInstance().getTime) - 1 to numRounds foreach {_ => - nextNode ! Token(nodeId) - } + Ring.startTime = System.currentTimeMillis() + nextNode ! Token(nodeId, numRounds) - case Token(id) => - if (id == nodeId) { - returnCount += 1 - if (returnCount == numRounds) { + case Token(goal, lapsRemaining) => + if (goal == nodeId) { + if (lapsRemaining == 1) { Ring.endTime = System.currentTimeMillis() println("Stop: \t" + Calendar.getInstance().getTime) - println(s"Duration: ${(Ring.endTime - Ring.startTime) / 1000.0}s") - System.exit(0) + println(s"Elapsed time: ${(Ring.endTime - Ring.startTime) / 1000.0}s") + system.shutdown() + } else { + nextNode ! Token(goal, lapsRemaining - 1) } + } else { + nextNode ! Token(goal, lapsRemaining) } - nextNode ! Token(id) } } - -} \ No newline at end of file +} diff --git a/Akka/src/main/scala/Ring2.scala b/Akka/src/main/scala/Ring2.scala deleted file mode 100644 index adbc8fd..0000000 --- a/Akka/src/main/scala/Ring2.scala +++ /dev/null @@ -1,71 +0,0 @@ -import java.util.Calendar - -import akka.actor.{Actor, ActorRef, ActorSystem, Props} - -/** - * @author chupanw - */ -object Ring2 { - - val system = ActorSystem() - - var startTime: Long = 0 - var endTime: Long = 0 - - def main(args: Array[String]): Unit = { - val numNodes = args(0).toInt - val numRounds = args(1).toInt - run(numNodes = numNodes, numRounds = numRounds) - } - - def run(numNodes: Int, numRounds: Int): Unit = { - val nodes = spawnNodes(numNodes, numRounds) - nodes(0) ! Start - } - - def spawnNodes(numNodes: Int, numRounds: Int): Array[ActorRef] = { - val nodes = - for (i <- 0 until numNodes) yield system.actorOf(Props(classOf[NodeActor2], i, numRounds), "Node" + i) - for (i <- 0 until numNodes) nodes(i) ! Connect(nodes((i + 1) % numNodes)) - nodes.toArray - } - - // Messages - case object Start - - case object Stop - - case class Connect(next: ActorRef) - - case class Token(id: Int, value: Int) - - class NodeActor2(val nodeId: Int, val numRounds: Int) extends Actor { - - var nextNode: ActorRef = context.system.deadLetters - - def receive = { - - case Connect(next: ActorRef) => -// println(s"Actor $nodeId is connecting to ${next.path}") - nextNode = next - - case Start => - Ring2.startTime = System.currentTimeMillis() - println("Start: \t" + Calendar.getInstance().getTime) - nextNode ! Token(nodeId, numRounds) - - case Token(id, value) => - if (value == 0) { - println(nodeId) - Ring2.endTime = System.currentTimeMillis() - println("Stop: \t" + Calendar.getInstance().getTime) - println(s"Duration: ${(Ring2.endTime - Ring2.startTime) / 1000.0}s") - System.exit(0) - } - else { - nextNode ! Token(id, value - 1) - } - } - } - -} diff --git a/README.md b/README.md index 7170927..28d2f33 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,57 @@ -# Actor Benchmark # +# ActorBench -There are 7 benchmark programs right now. Each of them has a Akka version (with Scala 2.11.8) and a Theater version (with Swift 3.0). +ActorBench is a suite of microbenchmarks designed to measure various aspects of an actor programming framework. Currently, implementations exist for: + +- Akka/Scala +- Theater/Swift + +See `READMD.md` under each implementation's directory for usage instructions. + +# Benchmarks + +## Ring + +This is an implementation of the Ring benchmark, as described in [*Programming Erlang*](http://pragprog.com/book/jaerlang/programming-erlang), for which there many solutions on the web. N actors are created in a ring. A message is sent around the ring for M times so that a total of N * M messages get sent. At all times, there is only one message in flight. + +Purpose: actor scheduling overhead + +## BusyRing + +This modified version of the Ring benchmark sends M messages along a ring of N actors. Each message travels along the ring exactly once. + +Purpose: throughput + +## Fork + +Starting from a root actor, each actor creates two child actors and form a binary tree. The parameter specifies the maximum depth of the tree. + +Purpose: actor creation time + +## TreeMsg + +This benchmark creates an actor tree and then send messages from root to leaves. The tree creation process is the same as in Fork. After actor tree is created, root actor sends messages to its children. Non-leaf nodes simply forward messages to their children, and leaf nodes send ACKs back to root node. If root node receives enough ACKs, it terminates the program. + +Purpose: efficiency of actor lookup process + +## Pipeline + +This benchmark simulates a 3-stage message processing pipeline. The pipeline looks like this: downloader -> indexer -> writer. In the beginning, request messages are sent to downloader. Each request message contains a string "Requested ". Downlaoder substitutes "Requested" with "Downloaded", and later indexer changes "Downloaded" to "Indexed", and finally writer changes "Indexed" to "Written". + +Purpose: throughput of stateless actors + +## Chameneos + +This is an implementation of the [Chameneos concurrency benchmark](https://benchmarksgame.alioth.debian.org/u64q/chameneosredux-description.html#chameneosredux). Two kinds of actor are created, one Mall actor and N Chameneos actors (N > 2). Chameneos meet other Chameneos at the Mall. A Chameneos indicates its wish to meet another Chameneos by sending a Meeting message to the Mall, which will either: + +1. put that Chameneos in a waiting slot if there is no other Chameneos waiting to meet, or +2. forward that Meeting message to the awaiting Chameneos. + +When two Chameneos meet, they change their colors (internal state) and then resume sending more Meeting requests to the Mall. The Mall can host at most M Chameneos at a time. If the limit is reached, the Mall tells all incoming Chameneos to stop. After getting exit confirmation from each Chameneos, the program stops. + +Purpose: throughput of stateful actors + +## Calculator + +A Master actor accepts requests and forward them to its workers randomly. The number of workers is specified by . When a worker receives the forwarded request from master, it generates a random arithmetic expression, computes the result, and increases the counter. Each random arithmetic expression contains basic arithmetic operators (e.g. +, -, \*, /). + +Purpose: scheduling of master/worker model