cats-effect: Невозможно увидеть уменьшение времени выполнения при использовании parSequence - PullRequest
0 голосов
/ 26 мая 2020

Я новичок в библиотеке cats-effect, и у меня возникла проблема с параллельным выполнением. У меня есть приложение, которое, на мой взгляд, принесет пользу, но когда я тестирую идею на игрушечной конструкции, я не вижу разницы во времени выполнения. Я чувствую, что мне не хватает чего-то очевидного для других, поэтому я решил попытать счастья. В приведенном ниже коде у меня есть две реализации суммирования последовательностей чисел (addInSequence и addInParallel), обе выполняются в функции run(). Когда я запускаю программу, я замечаю, что у них практически одинаковое время работы. Я упустил что-то очевидное? Однако в docs , похоже, не говорится о необходимости какой-либо дополнительной настройки, как и о других примерах, с которыми я встречался. Приветствуются любые идеи.

1 Ответ

3 голосов
/ 26 мая 2020

Две вещи:

  1. Не всегда гарантируется, что параллельные операции всегда будут быстрее. Если ваша последовательная операция коротка, то накладные расходы от отправки в несколько потоков и последующего сбора всех результатов могут быть больше, чем ускорение.

  2. Посмотрите, что вы измеряете. У вас есть одна последовательная операция, которая выполняет X объема работы, или 3 операции, которые выполняют X / 3 объема работы. Вы измеряете их все, а затем сравниваете: время выполнения X последовательно с общим временем выполнения X / 3 объема работы в 3 задачах. Если последовательный запуск занимал около 3 секунд, а каждый параллельный запуск занимал около 1 секунды, то logi c обе версии занимают 3 секунды. Что может быть правдой, если мы измеряем время использования ЦП, но не совсем, если мы измеряем время от начала всей этой работы до конца sh.

Если я запустил ваш код, я получу

@ ParallelMap.main(Array[String]())
List((Serial,Result(30,12.058612958004232)), (Parallel,Result(30,12.005087116995128)))

Однако, если я запустил этот код вместо:

object ParallelMap extends IOApp {

  def slowAdd(nums: Seq[Int]): Int = nums.foldLeft(0) {
    (out: Int, next: Int) =>
      val seconds: TimeUnit = java.util.concurrent.TimeUnit.SECONDS
      val delay: IO[Unit] = IO.sleep(FiniteDuration(1L, seconds))
      delay.unsafeRunSync()
      out + next
  }

  def timeIO[A](op: IO[A]): IO[Result[A]] = for {
    start <- IO(System.nanoTime / 1e9)
    out <- op
    stop = System.nanoTime / 1e9
  } yield Result(out, stop - start)


  def addInSequence(first: Seq[Int], second: Seq[Int], third: Seq[Int]): IO[Result[Int]] = {
    timeIO(IO(List(first, second, third).map(ns => slowAdd(ns)).sum))
  }

  def addInParallel(first: Seq[Int], second: Seq[Int], third: Seq[Int]): IO[Result[Int]] = {
    // I changed is as little as possible so that you would still see
    // similarity to your code, but normally I would write
    // .parTraverse(f) instead of .map(f).parSequence
    timeIO(List(first, second, third).map(ns => IO(slowAdd(ns))).parSequence.map(_.sum))
  }

  def run(args: List[String]): IO[ExitCode] = {
    val nums: Seq[Int] = 1 to 4
    val results: IO[Seq[(String, Result[Int])]] = for {
      serial <- addInSequence(nums, nums, nums)
      parallel <- addInParallel(nums, nums, nums)
    } yield Seq(("Serial", serial), ("Parallel", parallel))
    val report: IO[Unit] = results.map(println)
    report.unsafeRunSync()
    IO(ExitCode.Success)
  }
}

, чтобы измерить то, что, как мне кажется, вы хотели измерить, я получаю следующий результат:

@ ParallelMap.main(Array[String]())
List((Serial,Result(30,12.006349742005114)), (Parallel,Result(30,4.003020468982868)))

который показывает, что параллельные вычисления были в 3 раза быстрее, чем последовательные.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...