下面的示例代码。我有点好奇为什么 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.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/