我在实现 scala 期货时犯了一个错误,或者至少我认为我做到了,并且只是注意到它,当我修复错误时,它的运行速度比我不使用期货时慢得多。有人可以帮助我了解发生了什么吗?
我有一个慢速方法,需要运行 5,000 次。每个都是独立的并返回一个 Double。然后我需要计算 5,000 个返回值的平均值和标准差。
当我最初编码时,我是这样做的:
import actors.Futures._
import util.Random
import actors.Future
def one = {
var results = List[Future[Double]]()
var expectedResult: List[Double] = Nil
var i = 0
while (i < 1000) {
val f = future {
Thread.sleep(scala.util.Random.nextInt(5) * 100)
println("Loop count: " + i)
Random.nextDouble
}
results = results ::: List(f)
println("Length of results list: " + results.length)
results.foreach(future => {
expectedResult = future() :: expectedResult
i += 1
})
}
// I would return the list of Doubles here to calculate mean and StDev
println("### Length of final list: " + expectedResult.length)
}
我没有想到它,因为它运行得很快,我得到了我预期的结果。当我仔细查看它以尝试让它运行得更快(它没有使用我所有可用的 CPU 资源)时,我意识到我的循环计数器在错误的位置,并且foreach
在future
创建循环中并且因此,提前阻止了期货。或者我是这么想的。
我坚持了几个 println 语句,看看我是否能弄清楚发生了什么,并对发生的事情感到非常困惑......结果列表的长度与最终列表长度不匹配,也不匹配循环计数器!
我根据我认为(应该)发生的事情将我的代码修改为以下内容,并且事情变得慢得多,并且打印语句的输出与第一种方法相比没有任何意义。这次循环计数器似乎跳到了 1000,尽管最终列表长度是有意义的。
第二种方法确实使用了所有可用的 CPU 资源,这更符合我的预期,但我确信结果相同需要更长的时间。
def two = {
var results = List[Future[Double]]()
var expectedResult: List[Double] = Nil
var i = 0
while (i < 1000) {
val f = future {
Thread.sleep(scala.util.Random.nextInt(5) * 100)
println("Loop count: " + i)
Random.nextDouble
}
results = f :: results
i += 1
println("Length of results list: " + results.length)
}
results.foreach(future => {
expectedResult = future() :: expectedResult
})
// I would return the list of Doubles here to calculate mean and StDev
println("### Length of final list: " + expectedResult.length)
}
我在这里遗漏了一些明显的东西吗?
编辑
对于任何看到这个的人......问题是我正在将期货的结果重新添加到期货循环中的最终列表(expectedResult)中 - 正如 som-snytt 所指出的那样。
因此,在每个循环中,我都会反复迭代已完成的期货并获得:
//First Loop:
List(1)
//Second Loop:
List(1,2)
//Third Loop:
List(1,2,3,4)
//... and so on
最终列表中的模式是这样的:
List(n, n-1, n-2, ..., 4, 3, 2, 1, 3, 2, 1, 2, 1, 1)
由于列表有 5050 项长和 Double 值,当我只查看列表的开头时,很难看到模式。
最终,循环的数量实际上只有 100 个,而不是我需要的 5000 个。
该方法的第二版对于 scala 2.9 是正确的。