~zainab/blog

ref: 26a1999881d784f9cc3e2c0fdd1985585a645c8a blog/src/chapters/2022-02-12-cats-effect-ioruntime/snippets.out.md -rw-r--r-- 8.3 KiB
26a19998zainab-ali Remove the misplaced error outputted by mdoc 6 months ago

#sbt

ThisBuild / scalaVersion := "3.0.2"
ThisBuild / libraryDependencies +=
  "org.typelevel" %% "cats-effect" % "3.3.2"

#imports

import $ivy.`org.typelevel::cats-effect:3.3.2`

#setup

import cats.effect._
import cats.effect.unsafe._
import cats.effect.implicits._
import cats.implicits._

object Threading {

  val basicRuntime: IORuntime = IORuntime(
    compute = IORuntime.createDefaultBlockingExecutionContext("compute")._1,
    blocking = IORuntime.createDefaultBlockingExecutionContext("blocking")._1,
    scheduler = IORuntime.createDefaultScheduler()._1,
    shutdown = () => (),
    config = IORuntimeConfig()
  )

  def boundedRuntime(numThreads: Int): IORuntime = {
    lazy val lazyRuntime: IORuntime = {
      IORuntime(
        compute = IORuntime
          .createDefaultComputeThreadPool(lazyRuntime, numThreads, "compute")
          ._1,
        blocking =
          IORuntime.createDefaultBlockingExecutionContext("blocking")._1,
        scheduler = IORuntime.createDefaultScheduler()._1,
        shutdown = () => (),
        config = IORuntimeConfig()
      )
    }
    lazyRuntime
  }

  def time(work: IO[Unit]): IO[String] =
    work.timed.map {
      case (t, _) => s"The task took ${t.toSeconds} seconds."
    }
}
import Threading._

#snooze

val snooze: IO[Unit] = IO(Thread.sleep(2000L))

#run-snooze-no-runtime

time(snooze).unsafeRunSync()
// error:
// Could not find an implicit IORuntime.
// 
// Instead of calling unsafe methods directly, consider using cats.effect.IOApp, which
// runs your IO. If integrating with non-functional code or experimenting in a REPL / Worksheet,
// add the following import:
// 
// import cats.effect.unsafe.implicits.global
// 
// Alternatively, you can create an explicit IORuntime value and put it in implicit scope.
// This may be useful if you have a pre-existing fixed thread pool and/or scheduler which you
// wish to use to execute IO programs. Please be sure to review thread pool best practices to
// avoid unintentionally degrading your application performance.

#run-snooze

time(snooze).unsafeRunSync()(basicRuntime)
// res1: String = "The task took 2 seconds."

#snooze-list

val snoozes: List[IO[Unit]] = List(snooze, snooze)

#snooze-parallel

val parallelSnoozes: IO[Unit] = snoozes.parSequence.void

#snooze-parallel-run

time(parallelSnoozes).unsafeRunSync()(basicRuntime)
// res2: String = "The task took 2 seconds."

#snooze-parallel-thousand

val lotsOfSnoozes = List.fill(1000)(snooze).parSequence.void

#snooze-parallel-thousand-run

time(lotsOfSnoozes).unsafeRunSync()(basicRuntime)
// res3: String = "The task took 2 seconds."

#factorial

val factorial: IO[Unit] = {
  @scala.annotation.tailrec
  def go(n: Long, total: Long): Long =
    if (n > 1) go(n - 1, total * n) else total
  IO(go(2000000000L, 1)).void
}

#factorial-run

time(factorial).unsafeRunSync()(basicRuntime)
// res4: String = "The task took 2 seconds."

#factorial-io-parallelized

val factorials: IO[Unit] = List.fill(10)(factorial).parSequence.void

#factorial-io-parallelized-run

time(factorials).unsafeRunSync()(basicRuntime)
// res5: String = "The task took 3 seconds."

#runtime-available-processors

val numProcessors = Runtime.getRuntime().availableProcessors()
// numProcessors: Int = 8

#factorial-io-parallelized-time-each

val timedFactorial: IO[String] = time(factorial)
val timedFactorials: IO[List[String]] =
  List.fill(20)(timedFactorial).parSequence

#factorial-io-parallelized-time-each-run

timedFactorials.unsafeRunSync()(basicRuntime)
// res6: List[String] = List(
//   "The task took 6 seconds.",
//   "The task took 6 seconds.",
//   "The task took 6 seconds.",
//   "The task took 5 seconds.",
//   "The task took 5 seconds.",
//   "The task took 5 seconds.",
//   "The task took 5 seconds.",
//   "The task took 6 seconds.",
//   "The task took 6 seconds.",
//   "The task took 6 seconds.",
//   "The task took 6 seconds.",
//   "The task took 6 seconds.",
//   "The task took 5 seconds.",
//   "The task took 5 seconds.",
//   "The task took 6 seconds.",
//   "The task took 6 seconds.",
//   "The task took 4 seconds.",
//   "The task took 6 seconds.",
//   "The task took 5 seconds.",
//   "The task took 6 seconds."
// )

#factorial-bounded-threadpool

time(factorials).unsafeRunSync()(boundedRuntime(2))
// res7: String = "The task took 12 seconds."

#factorial-time-bounded-threadpool

timedFactorials.unsafeRunSync()(boundedRuntime(2))
// res8: List[String] = List(
//   "The task took 2 seconds.",
//   "The task took 2 seconds.",
//   "The task took 2 seconds.",
//   "The task took 2 seconds.",
//   "The task took 2 seconds.",
//   "The task took 2 seconds.",
//   "The task took 2 seconds.",
//   "The task took 2 seconds.",
//   "The task took 2 seconds.",
//   "The task took 2 seconds.",
//   "The task took 2 seconds.",
//   "The task took 2 seconds.",
//   "The task took 2 seconds.",
//   "The task took 2 seconds.",
//   "The task took 2 seconds.",
//   "The task took 2 seconds.",
//   "The task took 2 seconds.",
//   "The task took 2 seconds.",
//   "The task took 2 seconds.",
//   "The task took 2 seconds."
// )

#factorial-time-bounded-threadpool-20

timedFactorials.unsafeRunSync()(boundedRuntime(20))
// res9: List[String] = List(
//   "The task took 6 seconds.",
//   "The task took 5 seconds.",
//   "The task took 6 seconds.",
//   "The task took 6 seconds.",
//   "The task took 5 seconds.",
//   "The task took 6 seconds.",
//   "The task took 5 seconds.",
//   "The task took 6 seconds.",
//   "The task took 5 seconds.",
//   "The task took 5 seconds.",
//   "The task took 4 seconds.",
//   "The task took 5 seconds.",
//   "The task took 6 seconds.",
//   "The task took 6 seconds.",
//   "The task took 6 seconds.",
//   "The task took 5 seconds.",
//   "The task took 5 seconds.",
//   "The task took 6 seconds.",
//   "The task took 6 seconds.",
//   "The task took 6 seconds."
// )

#factorial-time-bounded-threadpool-available

timedFactorials.unsafeRunSync()(boundedRuntime(numProcessors))
// res10: List[String] = List(
//   "The task took 2 seconds.",
//   "The task took 2 seconds.",
//   "The task took 2 seconds.",
//   "The task took 2 seconds.",
//   "The task took 2 seconds.",
//   "The task took 2 seconds.",
//   "The task took 2 seconds.",
//   "The task took 2 seconds.",
//   "The task took 2 seconds.",
//   "The task took 2 seconds.",
//   "The task took 2 seconds.",
//   "The task took 2 seconds.",
//   "The task took 2 seconds.",
//   "The task took 2 seconds.",
//   "The task took 2 seconds.",
//   "The task took 2 seconds.",
//   "The task took 2 seconds.",
//   "The task took 2 seconds.",
//   "The task took 2 seconds.",
//   "The task took 2 seconds."
// )

#snooze-10

val tenSnoozes: IO[Unit] = List.fill(10)(snooze).parSequence.void

#snooze-10-run

time(tenSnoozes).unsafeRunSync()(boundedRuntime(numProcessors))
// res11: String = "The task took 4 seconds."

#combined

val snoozeAndCompute: IO[Unit] = 
  List(factorials, tenSnoozes).parSequence.void

#combined-run

time(snoozeAndCompute).unsafeRunSync()(boundedRuntime(numProcessors))
// res12: String = "The task took 6 seconds."

#setup-bounded-runtime

def boundedRuntime(numThreads: Int): IORuntime = 
  IORuntime(
    compute = IORuntime.createDefaultComputeThreadPool(numThreads),
    blocking = IORuntime.createDefaultBlockingExecutionContext()
  )

#compute-threadpool

boundedRuntime(numProcessors).compute
// res13: ExecutionContext = cats.effect.unsafe.WorkStealingThreadPool@4da4ceec

#better-snooze

val betterSnooze: IO[Unit] = IO.blocking(Thread.sleep(2000L))
val tenBetterSnoozes: IO[Unit] =
  List.fill(10)(betterSnooze).parSequence.void

#better-snooze-run

time(tenBetterSnoozes).unsafeRunSync()(boundedRuntime(numProcessors))
// res14: String = "The task took 2 seconds."

#combined-blocking

val betterSnoozeAndCompute: IO[Unit] =
  List(factorials, tenBetterSnoozes).parSequence.void

#combined-blocking-run

time(betterSnoozeAndCompute).unsafeRunSync()(
  boundedRuntime(numProcessors)
  )
// res15: String = "The task took 5 seconds."