Актор Scala с сохранением состояния, рекурсивный вызов быстрее, чем при использовании переменных?

Пример кода ниже. Мне немного любопытно, почему MyActor быстрее MyActor2. MyActor рекурсивно вызывает процесс / реакцию и сохраняет состояние в параметрах функции, тогда как MyActor2 сохраняет состояние в vars. MyActor даже имеет дополнительные накладные расходы на определение состояния, но все равно работает быстрее. Мне интересно, есть ли этому хорошее объяснение, или, может быть, я делаю что-то «не так».

Я понимаю, что разница в производительности незначительна, но тот факт, что она есть и постоянна, заставляет меня любопытствовать, что происходит здесь.

Игнорируя первые два прогона как разминку, я получаю:

MyActor: Мне немного любопытно, почему MyActor быстрее MyActor2. MyActor рекурсивно вызывает процесс / реакцию и сохраняет состояние в параметрах функции, тогда как MyActor2 сохраняет состояние в vars. MyActor даже имеет дополнительные накладные расходы на определение состояния, но все равно работает быстрее. Мне интересно, есть ли этому хорошее объяснение, или, может быть, я делаю что-то «не так».

Я понимаю, что разница в производительности незначительна, но тот факт, что она есть и постоянна, заставляет меня любопытствовать, что происходит здесь.

Игнорируя первые два прогона как разминку, я получаю:

MyActor: Мне немного любопытно, почему MyActor быстрее MyActor2. MyActor рекурсивно вызывает процесс / реакцию и сохраняет состояние в параметрах функции, тогда как MyActor2 сохраняет состояние в vars. MyActor даже имеет дополнительные накладные расходы на определение состояния, но все равно работает быстрее. Мне интересно, есть ли этому хорошее объяснение, или, может быть, я делаю что-то «не так».

Я понимаю, что разница в производительности незначительна, но тот факт, что она есть и постоянна, заставляет меня любопытствовать, что происходит здесь.

Игнорируя первые два прогона как разминку, я получаю:

MyActor: 559 511 544 529

против

MyActor2: 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
      }
    }
  }
}

РЕДАКТИРОВАТЬ: Основываясь на ответе Василия, я удалил цикл и попробовал снова. А затем MyActor2, основанный на vars, обошел стороной и теперь может быть примерно на 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()
    }
5
задан mentics 29 April 2011 в 16:47
поделиться