2009-12-11 16:37:44 +01:00
|
|
|
package test
|
|
|
|
|
|
|
|
|
|
import org.scalatest.junit.JUnitSuite
|
|
|
|
|
import org.junit.Test
|
2009-12-13 12:29:18 +01:00
|
|
|
import net.lag.logging.Logger
|
2009-12-11 16:37:44 +01:00
|
|
|
|
2009-12-13 12:29:18 +01:00
|
|
|
/**
|
|
|
|
|
* The Computer Language Benchmarks Game
|
|
|
|
|
* <p/>
|
|
|
|
|
* URL: [http://shootout.alioth.debian.org/]
|
|
|
|
|
* <p/>
|
|
|
|
|
* Contributed by Julien Gaugaz.
|
|
|
|
|
* <p/>
|
|
|
|
|
* Inspired by the version contributed by Yura Taras and modified by Isaac Gouy.
|
|
|
|
|
*/
|
2009-12-11 16:37:44 +01:00
|
|
|
class PerformanceTest extends JUnitSuite {
|
|
|
|
|
|
2009-12-13 12:29:18 +01:00
|
|
|
@Test
|
|
|
|
|
def benchAkkaActorsVsScalaActors = {
|
|
|
|
|
|
|
|
|
|
def stressTestAkkaActors(nrOfMessages: Int, nrOfActors: Int, sleepTime: Int): Long = {
|
|
|
|
|
import se.scalablesolutions.akka.actor.Actor
|
|
|
|
|
|
|
|
|
|
abstract class Colour
|
|
|
|
|
case object RED extends Colour
|
|
|
|
|
case object YELLOW extends Colour
|
|
|
|
|
case object BLUE extends Colour
|
|
|
|
|
case object FADED extends Colour
|
|
|
|
|
|
|
|
|
|
val colours = Array(BLUE, RED, YELLOW)
|
|
|
|
|
|
|
|
|
|
case class Meet(from: Actor, colour: Colour)
|
|
|
|
|
case class Change(colour: Colour)
|
|
|
|
|
case class MeetingCount(count: int)
|
|
|
|
|
case class ExitActor(actor: Actor, reason: String)
|
|
|
|
|
|
|
|
|
|
var totalTime = 0L
|
|
|
|
|
|
|
|
|
|
class Mall(var nrMeets: int, numChameneos: int) extends Actor {
|
|
|
|
|
var waitingChameneo: Option[Actor] = None
|
|
|
|
|
var sumMeetings = 0
|
|
|
|
|
var numFaded = 0
|
|
|
|
|
var startTime: Long = 0L
|
|
|
|
|
|
|
|
|
|
start
|
|
|
|
|
|
|
|
|
|
def startChameneos(): Unit = {
|
|
|
|
|
startTime = System.currentTimeMillis
|
|
|
|
|
var i = 0
|
|
|
|
|
while (i < numChameneos) {
|
|
|
|
|
Chameneo(this, colours(i % 3), i).start
|
|
|
|
|
i = i + 1
|
|
|
|
|
}
|
2009-12-11 16:37:44 +01:00
|
|
|
}
|
|
|
|
|
|
2009-12-13 12:29:18 +01:00
|
|
|
def receive = {
|
|
|
|
|
case MeetingCount(i) => {
|
|
|
|
|
numFaded = numFaded + 1
|
|
|
|
|
sumMeetings = sumMeetings + i
|
|
|
|
|
if (numFaded == numChameneos) {
|
|
|
|
|
totalTime = System.currentTimeMillis - startTime
|
|
|
|
|
println("time: " + totalTime)
|
|
|
|
|
exit
|
|
|
|
|
}
|
2009-12-11 16:37:44 +01:00
|
|
|
}
|
2009-12-13 12:29:18 +01:00
|
|
|
|
|
|
|
|
case msg@Meet(a, c) => {
|
|
|
|
|
if (nrMeets > 0) {
|
|
|
|
|
waitingChameneo match {
|
|
|
|
|
case Some(chameneo) =>
|
|
|
|
|
nrMeets = nrMeets - 1
|
|
|
|
|
chameneo ! msg
|
|
|
|
|
waitingChameneo = None
|
|
|
|
|
case None =>
|
|
|
|
|
waitingChameneo = sender
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
waitingChameneo match {
|
|
|
|
|
case Some(chameneo) =>
|
|
|
|
|
chameneo ! ExitActor(this, "normal")
|
|
|
|
|
case None =>
|
|
|
|
|
}
|
|
|
|
|
sender.get ! ExitActor(this, "normal")
|
|
|
|
|
}
|
2009-12-11 16:37:44 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-13 12:29:18 +01:00
|
|
|
case class Chameneo(var mall: Mall, var colour: Colour, cid: int) extends Actor {
|
|
|
|
|
var meetings = 0
|
2009-12-11 16:37:44 +01:00
|
|
|
|
2009-12-13 12:29:18 +01:00
|
|
|
override def start = {
|
|
|
|
|
val r = super.start
|
|
|
|
|
mall ! Meet(this, colour)
|
|
|
|
|
r
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override def receive: PartialFunction[Any, Unit] = {
|
|
|
|
|
case Meet(from, otherColour) =>
|
|
|
|
|
colour = complement(otherColour)
|
|
|
|
|
meetings = meetings + 1
|
|
|
|
|
from ! Change(colour)
|
|
|
|
|
mall ! Meet(this, colour)
|
|
|
|
|
case Change(newColour) =>
|
|
|
|
|
colour = newColour
|
|
|
|
|
meetings = meetings + 1
|
|
|
|
|
mall ! Meet(this, colour)
|
|
|
|
|
case ExitActor(_, _) =>
|
|
|
|
|
colour = FADED
|
|
|
|
|
sender.get ! MeetingCount(meetings)
|
|
|
|
|
exit
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def complement(otherColour: Colour): Colour = {
|
|
|
|
|
colour match {
|
|
|
|
|
case RED => otherColour match {
|
|
|
|
|
case RED => RED
|
|
|
|
|
case YELLOW => BLUE
|
|
|
|
|
case BLUE => YELLOW
|
|
|
|
|
case FADED => FADED
|
|
|
|
|
}
|
|
|
|
|
case YELLOW => otherColour match {
|
|
|
|
|
case RED => BLUE
|
|
|
|
|
case YELLOW => YELLOW
|
|
|
|
|
case BLUE => RED
|
|
|
|
|
case FADED => FADED
|
|
|
|
|
}
|
|
|
|
|
case BLUE => otherColour match {
|
|
|
|
|
case RED => YELLOW
|
|
|
|
|
case YELLOW => RED
|
|
|
|
|
case BLUE => BLUE
|
|
|
|
|
case FADED => FADED
|
|
|
|
|
}
|
|
|
|
|
case FADED => FADED
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override def toString() = cid + "(" + colour + ")"
|
|
|
|
|
}
|
2009-12-11 16:37:44 +01:00
|
|
|
|
2009-12-13 12:29:18 +01:00
|
|
|
val mall = new Mall(nrOfMessages, nrOfActors)
|
|
|
|
|
mall.startChameneos
|
|
|
|
|
Thread.sleep(sleepTime)
|
|
|
|
|
totalTime
|
2009-12-11 16:37:44 +01:00
|
|
|
}
|
|
|
|
|
|
2009-12-13 12:29:18 +01:00
|
|
|
def stressTestScalaActors(nrOfMessages: Int, nrOfActors: Int, sleepTime: Int): Long = {
|
|
|
|
|
var totalTime = 0L
|
|
|
|
|
|
|
|
|
|
import scala.actors._
|
|
|
|
|
import scala.actors.Actor._
|
|
|
|
|
|
|
|
|
|
abstract class Colour
|
|
|
|
|
case object RED extends Colour
|
|
|
|
|
case object YELLOW extends Colour
|
|
|
|
|
case object BLUE extends Colour
|
|
|
|
|
case object FADED extends Colour
|
|
|
|
|
|
|
|
|
|
val colours = Array(BLUE, RED, YELLOW)
|
|
|
|
|
|
|
|
|
|
case class Meet(colour: Colour)
|
|
|
|
|
case class Change(colour: Colour)
|
|
|
|
|
case class MeetingCount(count: int)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Mall(var n: int, numChameneos: int) extends Actor {
|
|
|
|
|
var waitingChameneo: Option[OutputChannel[Any]] = None
|
|
|
|
|
var startTime: Long = 0L
|
|
|
|
|
|
|
|
|
|
start()
|
|
|
|
|
|
|
|
|
|
def startChameneos(): Unit = {
|
|
|
|
|
startTime = System.currentTimeMillis
|
|
|
|
|
var i = 0
|
|
|
|
|
while (i < numChameneos) {
|
|
|
|
|
Chameneo(this, colours(i % 3), i).start()
|
|
|
|
|
i = i + 1
|
|
|
|
|
}
|
2009-12-11 16:37:44 +01:00
|
|
|
}
|
2009-12-13 12:29:18 +01:00
|
|
|
|
|
|
|
|
def act() {
|
|
|
|
|
var sumMeetings = 0
|
|
|
|
|
var numFaded = 0
|
|
|
|
|
loop {
|
|
|
|
|
react {
|
|
|
|
|
|
|
|
|
|
case MeetingCount(i) => {
|
|
|
|
|
numFaded = numFaded + 1
|
|
|
|
|
sumMeetings = sumMeetings + i
|
|
|
|
|
if (numFaded == numChameneos) {
|
|
|
|
|
totalTime = System.currentTimeMillis - startTime
|
|
|
|
|
exit()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case msg@Meet(c) => {
|
|
|
|
|
if (n > 0) {
|
|
|
|
|
waitingChameneo match {
|
|
|
|
|
case Some(chameneo) =>
|
|
|
|
|
n = n - 1
|
|
|
|
|
chameneo.forward(msg)
|
|
|
|
|
waitingChameneo = None
|
|
|
|
|
case None =>
|
|
|
|
|
waitingChameneo = Some(sender)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
waitingChameneo match {
|
|
|
|
|
case Some(chameneo) =>
|
|
|
|
|
chameneo ! Exit(this, "normal")
|
|
|
|
|
case None =>
|
|
|
|
|
}
|
|
|
|
|
sender ! Exit(this, "normal")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case class Chameneo(var mall: Mall, var colour: Colour, id: int) extends Actor {
|
|
|
|
|
var meetings = 0
|
|
|
|
|
|
|
|
|
|
def act() {
|
|
|
|
|
loop {
|
|
|
|
|
mall ! Meet(colour)
|
|
|
|
|
react {
|
|
|
|
|
case Meet(otherColour) =>
|
|
|
|
|
colour = complement(otherColour)
|
|
|
|
|
meetings = meetings + 1
|
|
|
|
|
sender ! Change(colour)
|
|
|
|
|
case Change(newColour) =>
|
|
|
|
|
colour = newColour
|
|
|
|
|
meetings = meetings + 1
|
|
|
|
|
case Exit(_, _) =>
|
|
|
|
|
colour = FADED
|
|
|
|
|
sender ! MeetingCount(meetings)
|
|
|
|
|
exit()
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-12-11 16:37:44 +01:00
|
|
|
}
|
2009-12-13 12:29:18 +01:00
|
|
|
|
|
|
|
|
def complement(otherColour: Colour): Colour = {
|
|
|
|
|
colour match {
|
|
|
|
|
case RED => otherColour match {
|
|
|
|
|
case RED => RED
|
|
|
|
|
case YELLOW => BLUE
|
|
|
|
|
case BLUE => YELLOW
|
|
|
|
|
case FADED => FADED
|
|
|
|
|
}
|
|
|
|
|
case YELLOW => otherColour match {
|
|
|
|
|
case RED => BLUE
|
|
|
|
|
case YELLOW => YELLOW
|
|
|
|
|
case BLUE => RED
|
|
|
|
|
case FADED => FADED
|
|
|
|
|
}
|
|
|
|
|
case BLUE => otherColour match {
|
|
|
|
|
case RED => YELLOW
|
|
|
|
|
case YELLOW => RED
|
|
|
|
|
case BLUE => BLUE
|
|
|
|
|
case FADED => FADED
|
|
|
|
|
}
|
|
|
|
|
case FADED => FADED
|
|
|
|
|
}
|
2009-12-11 16:37:44 +01:00
|
|
|
}
|
2009-12-13 12:29:18 +01:00
|
|
|
|
|
|
|
|
override def toString() = id + "(" + colour + ")"
|
2009-12-11 16:37:44 +01:00
|
|
|
}
|
2009-12-13 12:29:18 +01:00
|
|
|
|
|
|
|
|
val mall = new Mall(nrOfMessages, nrOfActors)
|
|
|
|
|
mall.startChameneos
|
|
|
|
|
Thread.sleep(sleepTime)
|
|
|
|
|
totalTime
|
2009-12-11 16:37:44 +01:00
|
|
|
}
|
|
|
|
|
|
2009-12-13 12:29:18 +01:00
|
|
|
Logger.INFO
|
|
|
|
|
println("===========================================")
|
|
|
|
|
println("== Benchmark Akka Actors vs Scala Actors ==")
|
2009-12-11 16:37:44 +01:00
|
|
|
|
2009-12-13 12:29:18 +01:00
|
|
|
var nrOfMessages = 2000000
|
|
|
|
|
var nrOfActors = 4
|
|
|
|
|
var akkaTime = stressTestAkkaActors(nrOfMessages, nrOfActors, 1000 * 20)
|
|
|
|
|
var scalaTime = stressTestScalaActors(nrOfMessages, nrOfActors, 1000 * 40)
|
|
|
|
|
var ratio: Double = scalaTime.toDouble / akkaTime.toDouble
|
2009-12-11 16:37:44 +01:00
|
|
|
|
2009-12-13 12:29:18 +01:00
|
|
|
println("\tNr of messages:\t" + nrOfMessages)
|
|
|
|
|
println("\tNr of actors:\t" + nrOfActors)
|
|
|
|
|
println("\tAkka Actors:\t" + akkaTime + "\t milliseconds")
|
|
|
|
|
println("\tScala Actors:\t" + scalaTime + "\t milliseconds")
|
|
|
|
|
println("\tAkka is " + ratio + " times faster\n")
|
|
|
|
|
println("===========================================")
|
2009-12-18 21:26:03 +01:00
|
|
|
assert(true)
|
2009-12-11 16:37:44 +01:00
|
|
|
}
|
|
|
|
|
}
|