【发布时间】:2012-02-29 17:19:14
【问题描述】:
我正在寻找机会在我的 Scala 2.9 / Akka 2.0 RC2 代码中提高并发性和性能。给定以下代码:
import akka.actor._
case class DataDelivery(data:Double)
class ComputeActor extends Actor {
var buffer = scala.collection.mutable.ArrayBuffer[Double]()
val functionsToCompute = List("f1","f2","f3","f4","f5")
var functionMap = scala.collection.mutable.LinkedHashMap[String,(Map[String,Any]) => Double]()
functionMap += {"f1" -> f1}
functionMap += {"f2" -> f2}
functionMap += {"f3" -> f3}
functionMap += {"f4" -> f4}
functionMap += {"f5" -> f5}
def updateData(data:Double):scala.collection.mutable.ArrayBuffer[Double] = {
buffer += data
buffer
}
def f1(map:Map[String,Any]):Double = {
// println("hello from f1")
0.0
}
def f2(map:Map[String,Any]):Double = {
// println("hello from f2")
0.0
}
def f3(map:Map[String,Any]):Double = {
// println("hello from f3")
0.0
}
def f4(map:Map[String,Any]):Double = {
// println("hello from f4")
0.0
}
def f5(map:Map[String,Any]):Double = {
// println("hello from f5")
0.0
}
def computeValues(immutableBuffer:IndexedSeq[Double]):Map[String,Double] = {
var map = Map[String,Double]()
try {
functionsToCompute.foreach(function => {
val value = functionMap(function)
function match {
case "f1" =>
var v = value(Map("lookback"->10,"buffer"->immutableBuffer,"parm1"->0.0))
map += {function -> v}
case "f2" =>
var v = value(Map("lookback"->20,"buffer"->immutableBuffer))
map += {function -> v}
case "f3" =>
var v = value(Map("lookback"->30,"buffer"->immutableBuffer,"parm1"->1.0,"parm2"->false))
map += {function -> v}
case "f4" =>
var v = value(Map("lookback"->40,"buffer"->immutableBuffer))
map += {function -> v}
case "f5" =>
var v = value(Map("buffer"->immutableBuffer))
map += {function -> v}
case _ =>
println(this.unhandled())
}
})
} catch {
case ex: Exception =>
ex.printStackTrace()
}
map
}
def receive = {
case DataDelivery(data) =>
val startTime = System.nanoTime()/1000
val answers = computeValues(updateData(data))
val endTime = System.nanoTime()/1000
val elapsedTime = endTime - startTime
println("elapsed time is " + elapsedTime)
// reply or forward
case msg =>
println("msg is " + msg)
}
}
object Test {
def main(args:Array[String]) {
val system = ActorSystem("actorSystem")
val computeActor = system.actorOf(Props(new ComputeActor),"computeActor")
var i = 0
while (i < 1000) {
computeActor ! DataDelivery(i.toDouble)
i += 1
}
}
}
当我运行它时,输出(转换为微秒)是
elapsed time is 4898
elapsed time is 184
elapsed time is 144
.
.
.
elapsed time is 109
elapsed time is 103
您可以看到 JVM 的增量编译器开始运行。
我认为一个快速的胜利可能是改变
functionsToCompute.foreach(function => {
到
functionsToCompute.par.foreach(function => {
但这会导致以下经过的时间
elapsed time is 31689
elapsed time is 4874
elapsed time is 622
.
.
.
elapsed time is 698
elapsed time is 2171
一些信息:
1) 我在具有 2 个内核的 Macbook Pro 上运行它。
2) 在完整版本中,函数是长时间运行的操作,循环遍历部分可变共享缓冲区。这似乎不是问题,因为从参与者的邮箱中检索消息正在控制流程,但我怀疑这可能是增加并发性的问题。这就是我转换为 IndexedSeq 的原因。
3) 在完整版中,functionsToCompute 列表可能会有所不同,因此并非 functionMap 中的所有项都必须被调用(即)functionMap.size 可能远大于 functionsToCompute.size
4) 函数可以并行计算,但结果映射必须在返回前完成
一些问题:
1) 如何让并行版本运行得更快?
2) 在哪里添加非阻塞和阻塞期货?
3) 将计算转发给另一个参与者有什么意义?
4) 提高不变性/安全性的机会有哪些?
谢谢, 布鲁斯
【问题讨论】:
-
我不确定你对你的答案做了什么......但第 4 点对我来说很有趣。看起来您可以在这里充分利用 akka.dispatch.Future.sequence。创建一个正在执行计算的 Future 列表,并使用序列将其转换为结果列表中的 Future。当未来返回时,将结果折叠到您需要的聚合地图/列表/容器中。
-
@DerekWyatt。在完整版中,答案将传递给另一个演员。感谢您的评论。
-
为什么每个功能没有一个演员?
-
@ViktorKlang。我想这是可能的,但我需要将所有函数评估的结果收集到一个映射中,然后传递给另一个参与者。 Futures 的 fork-join 功能不适合这个吗?不幸的是,我还没有足够的 Futures 经验,而且我不清楚您的建议将如何考虑到这一要求。能详细点吗?
-
@ViktorKlang。我想我需要花一些时间阅读 Akka 2.0 文档来编写 Futures...
标签: scala concurrency immutability akka par