performance - Scala stateful actor, recursive calling faster than using vars? -


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:

  1. actor scans mailbox;
  2. if finds message, schedules execution;
  3. 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