我必须在Scala中的2D列表上执行一些操作,并且我试图对该任务进行并行化。
目前我有三个期货,每个期货需要N行矩阵并执行必要的计算。它是这样写的:
val future1: Future[List[Int]] = Future { makeCalculations(0, 5) }
val future2: Future[List[Int]] = Future { makeCalculations(6, 10) }
val future3: Future[List[Int]] = Future { makeCalculations(11, 15) }
然后我使用for comprehension同时启动它们,产生返回值的列表。
问题是,我希望通过将Int传递给此函数并使其创建确切数量的期货来实现动态。
我试着让你理解产生期货,但看起来它们是按顺序开始的,我希望它们能够在同一时间开始。我在找错了地方吗?有更好的方法吗?
答案 0 :(得分:0)
首先,我认为您可能对期货如何安排感到困惑:
然后我使用for comprehension同时启动它们,产生返回值的列表。
事实上,Future
计划在创建后立即执行(apply
方法被调用)。
让我用一个小代码片段说明:
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration.Duration
import scala.concurrent.{Await, Future}
val start = System.currentTimeMillis()
val fs = (1 to 20).grouped(2).map { x =>
Future {
val ts = System.currentTimeMillis() - start
Thread.sleep(1000)
(x.head, x.head + x.length - 1, ts)
}
}
val res = Future.sequence(fs)
Await.result(res, Duration.Inf).foreach(
println
)
这里我有一个范围(1 to 20)
,我将其分成相等的部分并从每个部分创建Future
。每个未来都包含它的创建时间戳以及原始范围内的开始和结束索引。
你也可能会注意到期货内部存在延迟,所以如果它们按顺序执行,我们会看到开始时间的巨大差异;另一方面,如果期货同时并行开始,则开始时间戳几乎相同。
以下是我在我的机器上得到的结果(前两个数字是索引,第三个数字是相对开始时间戳):
(1,2,7)
(3,4,8)
(5,6,8)
(7,8,8)
(9,10,9)
(11,12,9)
(13,14,9)
(15,16,9)
(17,18,1011)
(19,20,1011)
如您所见,前8个期货同时开始,而第9个和第10个期货延迟1秒。
为什么会这样?因为我使用了scala.concurrent.ExecutionContext.Implicits.global
执行上下文,默认情况下其并行度等于处理器内核的数量(在我的情况下为8)。
让我们尝试为执行程序提供更高的并行度:
import java.util.concurrent.Executors
implicit val ex =
ExecutionContext.fromExecutor(Executors.newFixedThreadPool(512))
// same code as before
结果:
(1,2,8)
(3,4,8)
(5,6,16)
(7,8,13)
(9,10,13)
(11,12,14)
(13,14,14)
(15,16,14)
(17,18,15)
(19,20,16)
正如所料,所有期货大约同时开始。
最后的测试,让我们有一个并行的执行者:
implicit val ex =
ExecutionContext.fromExecutor(Executors.newFixedThreadPool(1))
结果:
(1,2,4)
(3,4,1005)
(5,6,2008)
(7,8,3009)
(9,10,4010)
(11,12,5014)
(13,14,6016)
(15,16,7016)
(17,18,8017)
(19,20,9019)
希望有助于了解Futures计划执行的时间以及如何控制并行性。随意在评论中提出任何问题。
UPD :如何使用Futures执行矩阵批处理的更清晰示例。
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration.Duration
import scala.concurrent.{Await, Future}
val m = List(
Array(1,2,3),
Array(4,5,6),
Array(7,8,9),
Array(1,2,3),
Array(4,5,6)
)
val n = 3 // desired number of futures
val batchSize = Math.ceil(m.size / n.toDouble).toInt
val fs:Iterator[Future[Int]] = m.grouped(batchSize).map {
rows:List[Array[Int]] =>
Future {
rows.map(_.sum).sum // any logic for the rows;
// sum of the elements
// as an illustration
}
}
// convert Iterator[Future[_]] to Future[Iterator[_]]
val res = Future.sequence(fs)
// print results
Await.result(res, Duration.Inf).foreach(
println
)
结果:
21 //sum of the elements of the first two rows
30 //... third and fourth row
15 //... single last row