以下示例代码。我有点好奇为什么MyActor比MyActor2更快。 MyActor以递归方式调用process / react并在函数参数中保持状态,而MyActor2将状态保持在vars中。 MyActor甚至有额外的开销状态,但仍然运行得更快。我想知道是否有一个很好的解释,或者我可能做了一些“错误的”。
我意识到性能差异并不显着,但它存在且一致的事实让我很好奇这里发生了什么。
忽略前两次作为预热,我得到:
MyActor: 559 511 544 529
VS
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
}
}
}
}
编辑:根据Vasil的回复,我删除了循环并再次尝试。然后基于vars的MyActor2超越了,现在可能会快10%左右。所以...经验教训是:如果你确信你最终不会有堆积溢出的积压消息,并且你想要挤出每一个小小的表现......不要使用循环而只是调用行为()方法递归。
更改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()
}
答案 0 :(得分:3)
这样的结果是由您的基准测试的细节引起的(许多小消息填充演员的邮箱比他们处理它们更快)。
通常,the workflow of react
如下:
Actor.suspendException
被抛出); 在第一种情况下,当处理程序完成处理消息时,执行直接进入react
方法,并且,只要邮箱中有大量消息,actor立即将下一条消息安排到执行,并且只在暂停之后。
在第二种情况下,loop
schedules the execution of react
是为了防止堆栈溢出(可能是您的Actor#1的情况,因为process
中的尾递归未被优化),因此,执行不会立即进行react
,如第一种情况。这就是毫无疑问的损失。
更新(取自here):
使用循环而不是递归反应 有效地使数量翻倍 线程池必须具有的任务 执行以完成 相同数量的工作,反过来 使它成为任何开销 调度程序更加明显 使用循环。
答案 1 :(得分:0)
在黑暗中疯狂刺伤。这可能是由于反应引发的异常以便撤离循环。异常创建非常繁重。但是我不知道它经常这样做,但应该可以检查一个捕获和一个计数器。
答案 2 :(得分:0)
测试开销在很大程度上取决于存在的线程数(尝试仅使用一个scala -Dactors.corePoolSize=1
的线程!)。我发现很难弄清楚差异的确切位置;唯一真正的区别在于,在一种情况下,您使用loop
,而在另一种情况下则不使用scala.actors.Scheduler$.impl
。循环做做了相当多的工作,因为它使用“andThen”重复创建函数对象而不是迭代。我不确定这是否足以解释差异,特别是考虑到ExceptionBlob
和{{1}}的大量使用。