-1

我正在阅读Akka cookbook并发现在一个示例中提高功能性能很有趣。我有下一个客户对象:

object HelloAkkaActorSystem extends App {
  implicit val timeout = Timeout(50 seconds)
  val actorSystem = ActorSystem("HelloAkka")
  val actor = actorSystem.actorOf(Props[FibonacciActor])
  // asking for result from actor
  val future = (actor ? 6).mapTo[Int]
  val st = System.nanoTime()
  val fiboacciNumber = Await.result(future, 60 seconds)
  println("Elapsed time: " + (System.nanoTime() - st) / math.pow(10, 6))
  println(fiboacciNumber)
}

以及actor类的两个实现。

第一的:

class FibonacciActor extends Actor {
  override def receive: Receive = {
    case num : Int =>
      val fibonacciNumber = fib(num)
      sender ! fibonacciNumber
  }
  def fib( n : Int) : Int = n match {
    case 0 | 1 => n
    case _ => fib( n-1 ) + fib( n-2 )
  }
}

第二:

class FibonacciActor extends Actor {
  override def receive: PartialFunction[Any, Unit] = {
    case num : Int =>
      val fibonacciNumber = fib(num)
      val s = sender()
      fibonacciNumber.onComplete {
        case Success(x) => s ! x
        case Failure(e) => s ! -1
      }
  }
  def fib( n : Int) : Future[Int] = n match {
    case 0 | 1 => Future{ n }
    case _ =>
      fib( n-1 ).flatMap(n_1 =>
        fib( n-2 ).map(n_2 =>
          n_1 + n_2))
  }
}

在我的机器First变体中,在 0.12 毫秒内执行,Second在 360 毫秒内。所以Second速度慢了 300 倍。使用htop我发现First在第二种情况下,变体使用 1 个核心对抗所有 4 个核心。是不是因为生成了太多异步任务?以及如何加快fib(n: Int)方法?

4

1 回答 1

1

首先,您的基准测试结果不太可能对现实有任何反映。在该示例中,JVM 可能会花费更多时间对实际运行的代码进行 JIT 处理。这是一篇关于创建微基准测试的好帖子:

如何在 Java 中编写正确的微基准测试?

通常,在 Java 中,您需要做大约 10000 次作为预热,以确保所有 JIT 编译都已发生(因为 JVM 会在您运行代码时分析您的代码,并且当它发现一个方法称为很多, 它停止了世界, 并将其编译成机器码而不是执行它. 在上面的基准测试中, 很少有代码会被编译成机器码, 它大部分会被解释, 这意味着它会运行得很慢,加上其中一些可能被检测为热点,因此您可以停止世界,编译,重新启动,这使其运行速度更慢。这就是为什么您应该循环运行它数千次以确保完成所有操作,在你真正开始计时之前。

其次,为了回答您的问题,在您的示例中,您将 fib 的每次执行(不是整个操作,而是执行 fib 的每次迭代)分派线程,该方法只需要几纳秒即可运行水池。将某些东西分派到线程池的开销是几微秒,而您发送线程池要做的事情只需要几纳秒来执行,因此您要支付 1000 倍的操作成本来“异步”执行它”。您应该只将计算量大的操作分派到线程池,例如需要几秒钟才能运行的操作,将非常小的操作分派到线程池是没有意义的。

于 2017-09-30T06:48:42.290 回答