Actors Ring

今度はRingにしてみた。

import scala.actors._
import scala.actors.Actor._

import java.lang.management._

object actor_ring extends Application {

  class myactor(no:int, nb:Actor, d:Boolean) extends Actor {
    var neighbour:Actor = nb
    var dec:boolean     = d
    var start_time:long = 0
    
    def this(no:int, nb:Actor) = this(no, nb, false)
    def setNeighbour(nb:Actor) = {neighbour = nb}
    def setStartTime(st:long)  = {start_time = st}
    
    def act() {
      react {
        case Tuple2('Msg, i:int) => {
          if (dec == true) {
            val m = ManagementFactory.getMemoryMXBean.getHeapMemoryUsage
            val now = System.nanoTime()
            println(((now - start_time) / 1000000000.0) + "[sec], " + m.getUsed/1024/1024 + "MB")
            start_time = now
            neighbour ! Tuple2('Msg, i-1)
          } else {
            neighbour ! Tuple2('Msg, i)
          }

          if (i == 1) exit()
          act()
        }
      }
    }
  }

  def make_chain(n:int):List[myactor] = {
    val first_at = new myactor(n, null, true)
    first_at.start
    make_chain(n-1, first_at, List(first_at))
  }
  
  def make_chain(n:int, next:myactor, chain:List[myactor]):List[myactor] = {
    n match {
      case 0 => chain
      case _ => {
        val at = new myactor(n, next)
        at.start
        make_chain(n-1, at, at :: chain)
      }
    }
  }

  val num      = 100000
  val loop_num = 10

  println("START")

  val m = ManagementFactory.getMemoryMXBean.getHeapMemoryUsage
  println(m.toString)
  val mnh = ManagementFactory.getMemoryMXBean.getNonHeapMemoryUsage
  println(mnh.toString)


  val st = System.nanoTime()
  val at_chain = make_chain(num)
  println*1

  at ! Tuple2('Msg, loop_num)
}

Pen4 2GHz OS:Ubuntu
Scala2.6.1 Final
において
10万Actorsを10周させてみると

START
init = 16777216(16384K) used = 573728(560K) committed = 16711680(16320K) max = 266403840(260160K)
init = 33718272(32928K) used = 14967328(14616K) committed = 34177024(33376K) max = 121634816(118784K)
15.669[microsec/create-actor]
1.210402832[sec], 25MB
1.364063691[sec], 26MB
1.02014446[sec], 32MB
0.80601815[sec], 40MB
1.301092184[sec], 29MB
0.763399419[sec], 37MB
1.25066676[sec], 26MB
0.78286096[sec], 34MB
1.243149875[sec], 24MB
0.718866898[sec], 27MB

な感じ。
ガベコレに0.5秒程度かかっている模様。

ちなみに、act関数の作りを

def act() {
  react {
    :
    act()
  }
}

から

def act() {
  loop {
    react {
      :
    }
  }
}

に変えると

START
init = 16777216(16384K) used = 573808(560K) committed = 16711680(16320K) max = 266403840(260160K)
init = 33718272(32928K) used = 14967432(14616K) committed = 34177024(33376K) max = 121634816(118784K)
17.997[microsec/create-actor]
1.640117266[sec], 30MB
1.117640128[sec], 42MB
1.78655447[sec], 33MB
1.131797293[sec], 43MB
1.672665112[sec], 34MB
1.128878803[sec], 44MB
1.676959597[sec], 36MB
1.102893978[sec], 48MB
1.720956371[sec], 37MB
0.775506848[sec], 41MB

となって、ちょっと遅い

def act() {
  receive {
    :
    act()
  }
}

にすると、
http://jijixi.azito.com/cgi-bin/diary/index.rb?date=20070622#p02
にも説明があるように、大量のスレッドが必要になるためか、かえってこない。
(というか、帰ってくるまで待てない。100くらいまでが限度)

*1:System.nanoTime()-st)/num/1000.0 + "[microsec/create-actor]") val at = at_chain.head at_chain.last.setNeighbour(at) at_chain.last.setStartTime(System.nanoTime(