我使用了取自ScalaMeter示例存储库的basic
示例(https://github.com/scalameter/scalameter-examples)。示例的代码如下:
measure method "map" in {
using(ranges) in { r =>
r.map(_ + 1)
}
}
在我的情况下,我想测试并行运行一些操作的加速率。一个简单的例子可以是,我划分了一个必须映射的范围。我使用Java8
中的ExecutorService
并行运行这些任务。它看起来像这样:
val cores = Runtime.getRuntime.availableProcessors()
val pool = Executors.newFixedThreadPool(cores)
measure method "parallel map" in {
using(ranges) in { r =>
val tasks = (0 until cores).map { t =>
new Callable[Unit] {
override def call() = (0 + t until r.last by cores).map(_ + 1)
}
}
import collection.JavaConverters._
pool.invokeAll(tasks.asJava)
}
}
问题是,尽管并行测试完成(您可以看到时间结果),但它不会返回退出代码。这意味着,如果我将Bench.LocalTime
更改为Bench.ForkedTime
,即使结果也会消失。我很困惑发生了什么事。有什么想法吗?
好吧,这很琐碎,因为我忘记了shutdown()
池。在invokeAll
之后添加它之后,我得到如下结果:
measure method "parallel map" in {
using(ranges) in { r =>
val pool = Executors.newFixedThreadPool(cores)
val tasks = (0 until cores).map { t =>
new Callable[Unit] {
override def call() = (0 + t until r.last by cores).map(_ + 1)
}
}
pool.invokeAll(tasks.asJava)
pool.shutdown()
}
}
唯一的问题是,现在,不仅要测量动作,还要测量ExecutorService
的创建时间并关闭它。但我想这暂时不会改变结果。
事实上,经过一段时间后,我发现了一种更简单、更"Scala"的方法来完成上述操作。您可以简单地创建一个任务列表作为要生成的函数列表(或者仍然可以是Callables
的列表),然后使用parallel
集合调用所有任务。代码如下:
measure method "parallel map" in {
using(ranges) in { r =>
val tasks = (0 until cores).flatMap { t =>
(0 + t until r by cores).map(i => () => i+1)
}
tasks.par.map(_.apply())
}
}
或者更容易,因为任务列表现在不关心cores
:
val tasks = (0 until r).map(i => () => i+1)