sample code below. i'm little curious why myactor faster myactor2. myactor recursively calls process/react , keeps state in function parameters whereas myactor2 keeps state in vars. myactor has overhead of tupling state still runs faster. i'm wondering if there explanation or if maybe i'm doing "wrong".
i realize performance difference not significant fact there , consistent makes me curious what's going on here.
ignoring first 2 runs warmup, get:
myactor: 559 511 544 529
vs.
myactor2: 647 613 654 610
import scala.actors._ object const { val num = 100000 val nm1 = num - 1 } trait send[messagetype] { def send(msg: messagetype) } // test 1 using recursive calls maintain state abstract class statefultypedactor[messagetype, statetype](val initialstate: statetype) extends actor send[messagetype] { def process(state: statetype, message: messagetype): statetype def act = proc(initialstate) def send(message: messagetype) = { ! message } private def proc(state: statetype) { react { case msg: messagetype => proc(process(state, msg)) } } } object myactor extends statefultypedactor[int, (int, long)]((0, 0)) { override def process(state: (int, long), input: int) = input match { case 0 => (1, system.currenttimemillis()) case input: int => state match { case (const.nm1, start) => println((system.currenttimemillis() - start)) (const.num, start) case (s, start) => (s + 1, start) } } } // test 2 using vars maintain state object myactor2 extends actor send[int] { private var state = 0 private var strt = 0: long def send(message: int) = { ! message } def act = loop { react { case 0 => state = 1 strt = system.currenttimemillis() case input: int => state match { case const.nm1 => println((system.currenttimemillis() - strt)) state += 1 case s => state += 1 } } } } // main: run testing object testactors { def main(args: array[string]): unit = { val = myactor // val = myactor2 a.start() testit(a) } def testit(a: send[int]) { (_ <- 0 5) { (i <- 0 const.num) { send } } } }
edit: based on vasil's response, removed loop , tried again. , myactor2 based on vars leapfrogged , might around 10% or faster. so... lesson is: if confident won't end stack overflowing backlog of messages, , care squeeze every little performance out... don't use loop , call act() method recursively.
change myactor2:
override def act() = react { case 0 => state = 1 strt = system.currenttimemillis() act() case input: int => state match { case const.nm1 => println((system.currenttimemillis() - strt)) state += 1 case s => state += 1 } act() }
such results caused specifics of benchmark (a lot of small messages fill actor's mailbox quicker can handle them).
generally, the workflow of react
following:
- actor scans mailbox;
- if finds message, schedules execution;
- when scheduling completes, or, when there're no messages in mailbox, actor suspends (
actor.suspendexception
thrown);
in first case, when handler finishes process message, execution proceeds straight react
method, and, long there're lots of messages in mailbox, actor schedules next message execute, , after suspends.
in second case, loop
schedules execution of react
in order prevent stack overflow (which might case actor #1, because tail recursion in process
not optimized), , thus, execution doesn't proceed react
immediately, in first case. that's millis lost.
update (taken here):
using loop instead of recursive react doubles number of tasks thread pool has execute in order accomplish same amount of work, in turn makes overhead in scheduler far more pronounced when using loop.
Comments
Post a Comment