のサイズが大きくない場合n
、シリアルコードは常に高速になります (末尾再帰の場合ははるかに高速です)。これは、新しい関数を呼び出す方が、新しいアクターを開始するよりも高速になるためです。さらに、スレッドとコンテキスト スイッチの間で競合が発生します。
以下のコードでは、 ごとに新しいアクターを開始しますn > 2
。最適化された方法はたくさんありますが、私は単純に繰り返しT(n) = T(n-1) + T(n-2)
をシリアルに使用しています。
import akka.actor.Actor
import akka.actor.Props
import akka.actor.ActorSystem
import akka.event.Logging
import akka.actor.ActorRef
import akka.routing.RoundRobinRouter
object Fib extends App {
trait Fib
case class N(val n: Int) extends Fib
case class Ans(n: Int)
class FibN(listen: ActorRef) extends Actor {
var numOfResults = 0;
var ans = 0;
val log = Logging(context.system, this)
def receive = {
case N(x) => {
//println(self.path+"-Message N(x) "+x)
val others = context.actorOf(Props(new FibN(self)).withRouter(RoundRobinRouter(2)), name = "Fibn:" + x)
if(x==1 || x==2)
listen ! new Ans(1)
else if(x>2){
others ! new N(x-1)
others ! new N(x-2)
}
}
case Ans(x) => {
//println(self.path+" Ans(x) "+x+" numOfResults "+numOfResults+" from "+sender.path)
numOfResults += 1
ans = ans + x;
if (numOfResults == 2){
//println(self.path+"sending back to sender "+listen.path+" numOfResults "+numOfResults)
listen ! Ans(ans)
}
}
case _ => println(self.path+"Not valid")
}
}
class Listener extends Actor{
val log = Logging(context.system, this)
var st:Long = 0;
def receive = {
case Ans(x) => {
println(self.path+"\n\nAns is "+x+" time taken: "+(System.currentTimeMillis() - st))
context.system.shutdown
}
case N(x) => {
println(self.path+" Message Received "+x)
val actor = context.actorOf(Props(new FibN(self)),"FibN")
st = System.currentTimeMillis()
actor ! new N(x)
}
case _ => println(self.path+" Invalid request")
}
}
val system = ActorSystem("Fibanoccia")
val listener = system.actorOf(Props[Listener],"Listener")
listener ! new N(25)
}
予想通り、これははるかに遅かったです。が非常に大きくない限りn
、アクターは前述の理由により常に遅くなります。より大きな「n」の場合、これは分解できます。