performance - Scala 有状态 Actor ,递归调用比使用 vars 更快?

标签 performance scala actor

下面的示例代码。我有点好奇为什么 MyActor 比 MyActor2 快。 MyActor 递归调用 process/react 并将状态保存在函数参数中,而 MyActor2 将状态保存在 vars 中。 MyActor 甚至有额外的开销来调整状态,但仍然运行得更快。我想知道是否对此有一个很好的解释,或者我是否在做一些“错误”的事情。

我意识到性能差异并不显着,但它存在且一致的事实让我很好奇这里发生了什么。

忽略前两次运行作为热身,我得到:

我的 Actor :
559
511
544
529

对比

我的 Actor 2:
647
613
654
610

import scala.actors._

object Const {
  val NUM = 100000
  val NM1 = NUM - 1
}

trait Send[MessageType] {
  def send(msg: MessageType)
}

// Test 1 using recursive calls to maintain state

abstract class StatefulTypedActor[MessageType, StateType](val initialState: StateType) extends Actor with Send[MessageType] {
  def process(state: StateType, message: MessageType): StateType

  def act = proc(initialState)

  def send(message: MessageType) = {
    this ! message
  }

  private def proc(state: StateType) {
    react {
      case msg: MessageType => proc(process(state, msg))
    }
  }
}

object MyActor extends StatefulTypedActor[Int, (Int, Long)]((0, 0)) {
  override def process(state: (Int, Long), input: Int) = input match {
    case 0 =>
      (1, System.currentTimeMillis())
    case input: Int =>
      state match {
        case (Const.NM1, start) =>
          println((System.currentTimeMillis() - start))
          (Const.NUM, start)
        case (s, start) =>
          (s + 1, start)
      }
  }
}

// Test 2 using vars to maintain state

object MyActor2 extends Actor with Send[Int] {
  private var state = 0
  private var strt = 0: Long

  def send(message: Int) = {
    this ! message
  }

  def act =
    loop {
      react {
        case 0 =>
          state = 1
          strt = System.currentTimeMillis()
        case input: Int =>
          state match {
            case Const.NM1 =>
              println((System.currentTimeMillis() - strt))
              state += 1
            case s =>
              state += 1
          }
      }
    }
}


// main: Run testing

object TestActors {
  def main(args: Array[String]): Unit = {
    val a = MyActor
    //    val a = MyActor2
    a.start()
    testIt(a)
  }

  def testIt(a: Send[Int]) {
    for (_ <- 0 to 5) {
      for (i <- 0 to Const.NUM) {
        a send i
      }
    }
  }
}

编辑:根据 Vasil 的回应,我删除了循环并再次尝试。然后基于 vars 的 MyActor2 实现了飞跃,现在可能快了 10% 左右。所以......教训是:如果你有信心最终不会导致堆栈溢出积压的消息,并且你想挤出每一个小性能......不要使用循环,只需调用act()递归的方法。

MyActor2 的更改:
  override def act() =
    react {
      case 0 =>
        state = 1
        strt = System.currentTimeMillis()
        act()
      case input: Int =>
        state match {
          case Const.NM1 =>
            println((System.currentTimeMillis() - strt))
            state += 1
          case s =>
            state += 1
        }
        act()
    }

最佳答案

这样的结果是由您的基准测试的细节引起的(许多小消息填充 Actor 的邮箱比它处理它们的速度更快)。

一般情况下,the workflow of react 如下:

  • Actor扫描邮箱;
  • 如果它找到一条消息,它schedules the execution ;
  • 当调度完成,或者邮箱中没有消息时,actor 挂起(抛出 Actor.suspendException);

  • 在第一种情况下,当处理程序完成处理消息时,执行直接进行到 react方法,并且,只要邮箱中有很多消息,actor 就会立即安排下一条消息执行,并且只有在挂起之后才会执行。

    在第二种情况下, loop schedules the execution of react 为了防止堆栈溢出(这可能是 Actor #1 的情况,因为 process 中的尾递归未优化),因此,执行不会继续到 react立即,就像在第一种情况下一样。这就是毫厘丢失的地方。

    更新(取自 here):

    Using loop instead of recursive react effectively doubles the number of tasks that the thread pool has to execute in order to accomplish the same amount of work, which in turn makes it so any overhead in the scheduler is far more pronounced when using loop.

    关于performance - Scala 有状态 Actor ,递归调用比使用 vars 更快?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5826111/

    相关文章:

    Scala:为什么Actors 是轻量级的?

    Akka 2.6 如何在 Akka Typed 中读取 `Dead Letters`?

    c++ - 为什么 map::operator[] 设计缓慢?

    Java 应用程序/ArrayList 与直接数据库查询

    django - 如何为可能的斜线打点 Django 做好准备?

    json - Scala - 如何将 json 作为输入参数并解析它?

    scala - 在 Play 2.5 中编写 BodyParser

    python - 为什么在循环调用时 plt.savefig() 性能会下降?

    java - clojure 中的 Scala 类

    scala - akka actor 中的消息传递序列