13
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Futureによる演算とActorを使った演算のコスト比較

Posted at

Effective Akkaの中で、「askを使うよりもなるべくtellを使いなさい」と言われる理由の一つとしてコストが挙げられていたけれども、実際のところどれだけコストが違うのかというところをfutureベースのコード(≒ ask)とtellベースのコードを作って確かめてみた。

futureベースのサンプルコード
import java.lang.management.ManagementFactory

import scala.concurrent.{Await, ExecutionContext, Future}
import scala.concurrent.duration.Duration
import scala.concurrent.forkjoin.ForkJoinPool
import scala.util.Random

object FutureSeqSample extends App {
  val pid = ManagementFactory.getRuntimeMXBean.getName.split('@').head
  println(s"jmap -histo:live ${pid}")
  val forkJoinExecutor = new ForkJoinPool(10)
  implicit val ec = ExecutionContext.fromExecutor(forkJoinExecutor)

  val futures = for (i <- 1 to 100000) yield {
    Future {
      Thread.sleep(300000000L)
      Random.nextInt()
    }
  }
  val f = Future.sequence(futures)
  f onSuccess {
    case seq => println(s"sum is ${seq.reduceLeft(_+_)}")
  }
  Await.result(f, Duration.Inf)
  Thread.sleep(30000000L)
}
tellベースのサンプルコード
import java.lang.management.ManagementFactory

import scala.concurrent.Await
import scala.concurrent.duration.{Duration, DurationInt}
import scala.util.{Random, Success}

import akka.actor.{Actor, ActorRef, ActorSystem, Props, Stash, actorRef2Scala}
import akka.pattern.ask
import akka.routing.RoundRobinPool
import akka.util.Timeout

object ActorSeqSample extends App {
  val pid = ManagementFactory.getRuntimeMXBean.getName.split('@').head
  println(s"jmap -histo:live ${pid}")

  implicit val timeout = Timeout(100 seconds)
  val system = ActorSystem()
  import system.dispatcher
  
  val executor = system.actorOf(Props[Executor])
  val result = executor ? Start(100000)
  result.mapTo[CalculateResult] onComplete {
    case Success(sum) => println(s"sum is ${sum.n}")
    case _ => // このサンプルでは失敗しない
  }
  Await.result(result, Duration.Inf)
  Thread.sleep(300000L)
}

case class Start(n: Int)
case class CalculateResult(n: Int)

class Executor extends Actor with Stash {
  var sum = 0
  var resultCount = 0
  var maxCount = 0
  val randomIntActors = context.actorOf(new RoundRobinPool(10).props(Props[RandomIntActor]))
  
  def receive: Actor.Receive = {
    case Start(n) =>
      for (i <- 1 to n) {
        randomIntActors ! "do it"  
      }
      sum = 0
      resultCount = 0
      maxCount = n
      context become calculate(sender)
  }
  
  def calculate(originalSender: ActorRef): Actor.Receive = {
    case CalculateResult(n) =>
      println(n)
      resultCount += 1
      if (resultCount >= maxCount) {
        originalSender ! CalculateResult(sum)
        context.unbecome()
        unstashAll()
      }
    case _ => stash()
  }
}

class RandomIntActor extends Actor {
  def receive = {
    case msg =>
      Thread.sleep(3000000L)
      sender ! CalculateResult(Random.nextInt())
  }
}
計測結果(jmap)
  • future
>jmap -histo:live 10208 | find "00"
   1:        200001        3200016  scala.concurrent.impl.Promise$DefaultPromise
   2:        100010        2400240  scala.collection.immutable.$colon$colon
   3:        100003        2400072  scala.concurrent.impl.CallbackRunnable
   4:        100001        2400024  scala.concurrent.impl.ExecutionContextImpl$AdaptedForkJoinTask
   5:        100000        2400000  scala.concurrent.Future$$anonfun$flatMap$1
   6:        100000        2400000  scala.concurrent.Future$$anonfun$sequence$1$$anonfun$apply$10
   7:        100000        2400000  scala.concurrent.impl.Future$PromiseCompletingRunnable
   8:        100000        1600000  future.samples.FutureSeqSample$$anonfun$2$$anonfun$apply$1
  • tell
>jmap -histo:live 5540 | find "00"
   1:        100007        2400168  java.util.concurrent.ConcurrentLinkedQueue$Node
   2:        100000        2400000  akka.dispatch.Envelope

サンプルは10万回の演算命令ですが、futureベースでやろうとした場合、その時点で結果を受け取るための様々なオブジェクトが作られてしまうけれども、tellベース場合はmailboxにメッセージを入れるコストくらいしかかかっていない、ということが言えそうです。

13
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?