Jestem więc zdziwiony, dlaczego mapowanie nad iteratorem z futures sprawi, że działa sekwencyjnie. Rozważ następujący kod -

import org.scalameter._

object IteratorVsListInFutures extends App {
  def slowFunction = Thread.sleep(1000)

  import scala.concurrent.ExecutionContext.Implicits.global
  import scala.concurrent._
  import duration._

  println("Should take approximately 4000 ms or 4 sec")
  println{
    withWarmer(new Warmer.Default) measure {
      List(1,2,3,4).foreach(_ => slowFunction)
    }
  }

  println("Should take approximately 1 second")
  println {
    withWarmer(new Warmer.Default) measure {
      val futures: Seq[Future[Unit]] = List(1,2,3,4).map(_ => Future { slowFunction})
      futures.foreach(x => Await.result(x, 10.seconds))
    }
  }

  println("And how long does this take")
  println {
    withWarmer(new Warmer.Default) measure {
      val futures = List(1,2,3,4).iterator.map(_ => Future { slowFunction})
      futures.foreach(x => Await.result(x, 10.seconds))
    }
  }

}

Dostaję następujące wyniki -

Should take approximately 4000 ms or 4 sec
4012.132085 ms
Should take approximately 1 second
1004.997573 ms
And how long does this take
4016.533206 ms

Process finished with exit code 0

Pierwszy benchmark jest taki, jak przewidziano około 4 sekund, drugi benchmark jest również przewidziany około 1 sekundy (ponieważ kontrakty terminowe są wykonywane równolegle), co mnie mylą, dlatego, że trzeci benchmark też jest około 4 sekundy?

2
Dhruv Kapur 12 styczeń 2020, 21:49

1 odpowiedź

Najlepsza odpowiedź

Ze względu na Nie-ścisłe natura iteratory następujące

List(1,2,3,4).iterator.map(_ => Future { slowFunction })

Oceniają Iterator[Future[Unit]], co jest tylko opis transformacji, które się stało, ale jeszcze się nie stało. Kluczem jest zrozumienie

Leniwe kolekcje są szczególnie przydatne do opisania kolejnych Operacje transformacji bez oceny pośredniego Transformacje

Nieco przepiszmy swój przykład, aby podkreślić transformację pośrednią

List(1,2,3,4)
  .iterator
  .map(_ => Future { slowFunction })         // <-- intermediate transformation
  .foreach(x => Await.result(x, 10.seconds))

Tak więc map(_ => Future { slowFunction }) jest transformacją pośrednią, która nie ocenia się, jednak jest to transformacja, która miała kopać futures. Zamiast tego transformacja map jest połączona z transformacją foreach, która ma być wykonana jako jedna pojedyncza transformacja

 List(1,2,3,4)
   .foreach(_ => Await.result(Future { slowFunction }, 10.seconds) )

Teraz staje się jasne, że czekamy na zakończenie Future, zanim przejdziemy do następnego elementu, stąd {x1}} s seryjnie.

5
Mario Galic 12 styczeń 2020, 21:30