可变数量的期货

时间:2017-06-11 16:37:57

标签: multithreading scala asynchronous multidimensional-array future

我必须在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传递给此函数并使其创建确切数量的期货来实现动态。

我试着让你理解产生期货,但看起来它们是按顺序开始的,我希望它们能够在同一时间开始。我在找错了地方吗?有更好的方法吗?

1 个答案:

答案 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