package exercise.actors
import scala.actors.Actor
import scala.actors.Actor._
import scala.collection.mutable.Queue;
import scala.actors.TIMEOUT
case class Done(diff:Long)
object ExpRandom {
def rand(m:Double):Double = {
-m * Math.log(Math.random)
}
}
class Service(observer: Observer) extends Actor {
val μ = 0.5 //平均サービス率: 平均サービス時間2秒の逆数
val queue = new Queue[Long]
def serviceTime(): Double = {
ExpRandom.rand(1.0 / μ)
}
val worker: Actor = new Actor {
def act() {
loop {
receive {
case 'serve =>
Thread.sleep(Math.round(serviceTime() * 1000))
sender ! 'afterProcessed
}
}
}
}
worker.start
def act() {
var inService: Boolean = false
loop {
receive {
case 'customerIn =>
queue.enqueue(System.currentTimeMillis())
Console.println("queue.length=" + queue.length)
self! 'doService
case 'afterProcessed =>
inService = false
queue.dequeue
self! 'doService
case 'doService =>
if (!inService) inService = doService()
}
}
}
def doService(): Boolean = {
if (queue.isEmpty) false
else {
val elapsed = System.currentTimeMillis() - queue.front
observer! Done(elapsed)
worker! 'serve
true
}
}
}
class Customer(service: Service) extends Actor {
val λ = 0.4 // 毎秒0.4個のリクエストが到着
var interval: Double = 0
def arrivalInterval(): Double = {
ExpRandom.rand(1.0 / λ)
}
def act() {
loop {
receiveWithin(Math.round(interval * 1000L)) {
case TIMEOUT =>
service! 'customerIn
interval = arrivalInterval()
}
}
}
}
class Observer extends Actor {
var requestCount:Int = 0
var totalWaitingTime:Double = 0
def act() {
loop {
receive {
case Done(time)=>
totalWaitingTime += time
requestCount += 1
Console.printf(
"リクエスト%d; 待ち時間:%dms; 平均待ち時間=%dms;%n",
requestCount,
time,
Math.round(totalWaitingTime / requestCount))
}
}
}
}
object QueueTheoryTest extends Application {
val observer = new Observer
val service = new Service(observer)
val customer = new Customer(service)
observer.start
service.start
customer.start
}(シンボルを表すアポストロフィが文字列をくくるクオーテーションマークに認識されているらしく、Prettify が上手く動かない・・・)
actor customerから平均到着率(λ)0.4個/秒でリクエストが届き、actor service が平均サービス率(μ)0.5個/秒でリクエストを処理する設定にしてみた。ρ=0.4/0.5、Ts=1/0.5 なので、平均待ち時間は Tw = ρ / (1 - ρ) * Ts = 8秒 = 8000ms となることが期待されるが、以下のような結果になった。リクエスト1998; 待ち時間:19875ms; 平均待ち時間=8527ms; queue.length=10 リクエスト1999; 待ち時間:25484ms; 平均待ち時間=8535ms; リクエスト2000; 待ち時間:25047ms; 平均待ち時間=8544ms; リクエスト2001; 待ち時間:25266ms; 平均待ち時間=8552ms; queue.length=8 リクエスト2002; 待ち時間:22531ms; 平均待ち時間=8559ms;なんか微妙。期待値8000msより結構大きい値になった。リクエスト2000個でもなかなか予想する値に落ち着かない。λ=2、μ=2.5にして回転率を上げてみるとリクエスト5000個で期待値1600ms に対して 1455ms と少し小さめの数値になった。なんだろう?もっと待てば予想する値に近づくのか、Java の乱数の分布やJava/Scalaのリアルタイム処理のクセなのか、あるいはコードに間違いがあるのか良くわからない。ただ今回は Actor の使い方がざっくりと解ればいいので、余り深く突っ込むのは止めておいた。
Erlang から Scala に移植するに当たって、erlang:send_after に当たるものが、どうも Scalaには見当たらなかったので、もう一つの Actor を使ってThread.sleepさせてメッセージ送信を遅延させるコードを書いた。「actor {}」ではなくて「new Actor」を使っているが、前者だと上手く動かず、調べておきたいところだが今回は時間切れ。あと react を使ってメッセージをやりとりしているときにThread.sleep()すると、他の Actorまで止まってしまうのだろうと予想していたが、この実験ではreceive でも react でもあまりはっきりした違いは見られなかった。後でちゃんと調べておこう。
まあ、せっかく勉強し始めた Scala の忘却防止と、少量の知識追加としては、今回はこんなもんだろう。あと Scala に移植しているうちに Erlang側コードに間違いやらまずいコードがあったのに気付いたので、これも後で直しておく。
0 件のコメント:
コメントを投稿