Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions benchmarks/src/main/scala/scalaz/effect/IOBenchmarks.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright (C) 2017 John A. De Goes. All rights reserved.
package scalaz.effect

object IOBenchmarks extends RTS {
import monix.execution.Scheduler

implicit val monixScheduler: Scheduler = {
import monix.execution.ExecutionModel.SynchronousExecution
Scheduler.computation().withExecutionModel(SynchronousExecution)
}

class Thunk[A](val unsafePerformIO: () => A) {
def map[B](ab: A => B): Thunk[B] =
new Thunk(() => ab(unsafePerformIO()))
def flatMap[B](afb: A => Thunk[B]): Thunk[B] =
new Thunk(() => afb(unsafePerformIO()).unsafePerformIO())
def attempt: Thunk[Either[Throwable, A]] = new Thunk(() => {
try Right(unsafePerformIO())
catch {
case t : Throwable => Left(t)
}
})
}
object Thunk {
def apply[A](a: => A): Thunk[A] = new Thunk(() => a)

def fail[A](t: Throwable): Thunk[A] = new Thunk(() => throw t)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Copyright (C) 2017 John A. De Goes. All rights reserved.
package scalaz.effect

import java.util.concurrent.TimeUnit
import org.openjdk.jmh.annotations._
import scala.concurrent.Await

import IOBenchmarks._

@State(Scope.Thread)
@BenchmarkMode(Array(Mode.Throughput))
@OutputTimeUnit(TimeUnit.SECONDS)
class IODeepAttemptBenchmark {
@Param(Array("1000"))
var depth: Int = _

def halfway = depth / 2

@Benchmark
def thunkDeepAttempt(): BigInt = {
def descend(n: Int): Thunk[BigInt] =
if (n == depth) Thunk.fail(new Error("Oh noes!"))
else if (n == halfway) descend(n + 1).attempt.map(_.fold(_ => 50, a => a))
else descend(n + 1).map(_ + n)

descend(0).unsafePerformIO()
}

@Benchmark
def futureDeepAttempt(): BigInt = {
import scala.util.Success
import scala.concurrent.Future
import scala.concurrent.duration.Duration.Inf

def descend(n: Int): Future[BigInt] =
if (n == depth) Future.failed(new Exception("Oh noes!"))
else if (n == halfway) descend(n + 1).transform(_.transform(Success(_), _ => Success(50)))
else descend(n + 1).map(_ + n)

Await.result(descend(0), Inf)
}

@Benchmark
def monixDeepAttempt(): BigInt = {
import monix.eval.Task

def descend(n: Int): Task[BigInt] =
if (n == depth) Task.raiseError(new Error("Oh noes!"))
else if (n == halfway) descend(n + 1).attempt.map(_.fold(_ => 50, a => a))
else descend(n + 1).map(_ + n)

descend(0).runSyncMaybe.right.get
}

@Benchmark
def scalazDeepAttempt(): BigInt = {
def descend(n: Int): IO[BigInt] =
if (n == depth) IO.fail(new Error("Oh noes!"))
else if (n == halfway) descend(n + 1).attempt.map(_.fold[BigInt](_ => 50)(a => a))
else descend(n + 1).map(_ + n)

unsafePerformIO(descend(0))
}

@Benchmark
def catsDeepAttempt(): BigInt = {
import cats.effect._

def descend(n: Int): IO[BigInt] =
if (n == depth) IO.raiseError(new Error("Oh noes!"))
else if (n == halfway) descend(n + 1).attempt.map(_.fold(_ => 50, a => a))
else descend(n + 1).map(_ + n)

descend(0).unsafeRunSync()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright (C) 2017 John A. De Goes. All rights reserved.
package scalaz.effect

import java.util.concurrent.TimeUnit
import org.openjdk.jmh.annotations._
import scala.concurrent.Await

import IOBenchmarks._

@State(Scope.Thread)
@BenchmarkMode(Array(Mode.Throughput))
@OutputTimeUnit(TimeUnit.SECONDS)
class IODeepFlatMapBenchmark {
@Param(Array("20"))
var depth: Int = _

@Benchmark
def thunkDeepFlatMap(): BigInt = {
def fib(n: Int): Thunk[BigInt] =
if (n <= 1) Thunk(n) else
fib(n-1).flatMap { a =>
fib(n-2).flatMap(b => Thunk(a + b))
}

fib(depth).unsafePerformIO()
}

@Benchmark
def futureDeepFlatMap(): BigInt = {
import scala.concurrent.Future
import scala.concurrent.duration.Duration.Inf

def fib(n: Int): Future[BigInt] =
if (n <= 1) Future(n) else
fib(n-1).flatMap { a =>
fib(n-2).flatMap(b => Future(a + b))
}

Await.result(fib(depth), Inf)
}

@Benchmark
def monixDeepFlatMap(): BigInt = {
import monix.eval.Task

def fib(n: Int): Task[BigInt] =
if (n <= 1) Task.eval(n) else
fib(n-1).flatMap { a =>
fib(n-2).flatMap(b => Task.eval(a + b))
}

fib(depth).runSyncMaybe.right.get
}

@Benchmark
def scalazDeepFlatMap(): BigInt = {
def fib(n: Int): IO[BigInt] =
if (n <= 1) IO.point(n) else
fib(n-1).flatMap { a =>
fib(n-2).flatMap(b => IO.point(a + b))
}

unsafePerformIO(fib(depth))
}

@Benchmark
def catsDeepFlatMap(): BigInt = {
import cats.effect._

def fib(n: Int): IO[BigInt] =
if (n <= 1) IO(n) else
fib(n-1).flatMap { a =>
fib(n-2).flatMap(b => IO(a + b))
}

fib(depth).unsafeRunSync
}
}
76 changes: 76 additions & 0 deletions benchmarks/src/main/scala/scalaz/effect/IOLeftBindBenchmark.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Copyright (C) 2017 John A. De Goes. All rights reserved.
package scalaz.effect

import java.util.concurrent.TimeUnit
import org.openjdk.jmh.annotations._
import scala.concurrent.Await

import IOBenchmarks._

@State(Scope.Thread)
@BenchmarkMode(Array(Mode.Throughput))
@OutputTimeUnit(TimeUnit.SECONDS)
class IOLeftBindBenchmark {
@Param(Array("10000"))
var size: Int = _

@Param(Array("100"))
var depth: Int = _

@Benchmark
def thunkLeftBindBenchmark(): Int = {
def loop(i: Int): Thunk[Int] =
if (i % depth == 0) Thunk(i + 1).flatMap(loop)
else if (i < size) loop(i + 1).flatMap(i => Thunk(i))
else Thunk(i)

Thunk(0).unsafePerformIO()
}

@Benchmark
def futureLeftBindBenchmark(): Int = {
import scala.concurrent.Future
import scala.concurrent.duration.Duration.Inf

def loop(i: Int): Future[Int] =
if (i % depth == 0) Future(i + 1).flatMap(loop)
else if (i < size) loop(i + 1).flatMap(i => Future(i))
else Future(i)

Await.result(Future(0).flatMap(loop), Inf)
}

@Benchmark
def monixLeftBindBenchmark(): Int = {
import monix.eval.Task

def loop(i: Int): Task[Int] =
if (i % depth == 0) Task.eval(i + 1).flatMap(loop)
else if (i < size) loop(i + 1).flatMap(i => Task.eval(i))
else Task.eval(i)

Task.eval(0).flatMap(loop).runSyncMaybe.right.get
}

@Benchmark
def scalazLeftBindBenchmark(): Int = {
def loop(i: Int): IO[Int] =
if (i % depth == 0) IO.point(i + 1).flatMap(loop)
else if (i < size) loop(i + 1).flatMap(i => IO.point(i))
else IO.point(i)

unsafePerformIO(IO.point(0).flatMap(loop))
}

@Benchmark
def catsLeftBindBenchmark(): Int = {
import cats.effect._

def loop(i: Int): IO[Int] =
if (i % depth == 0) IO(i + 1).flatMap(loop)
else if (i < size) loop(i + 1).flatMap(i => IO(i))
else IO(i)

IO(0).flatMap(loop).unsafeRunSync
}
}
74 changes: 74 additions & 0 deletions benchmarks/src/main/scala/scalaz/effect/IOMapBenchmark.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright (C) 2017 John A. De Goes. All rights reserved.
package scalaz.effect

import java.util.concurrent.TimeUnit
import org.openjdk.jmh.annotations._
import scala.concurrent.Await
import scala.annotation.tailrec

import IOBenchmarks._

@State(Scope.Thread)
@BenchmarkMode(Array(Mode.Throughput))
@OutputTimeUnit(TimeUnit.SECONDS)
class IOMapBenchmark {
@Param(Array("500"))
var depth: Int = _

@Benchmark
def thunkMap(): BigInt = {
@tailrec
def sumTo(t: Thunk[BigInt], n: Int): Thunk[BigInt] =
if (n <= 1) t
else sumTo(t.map(_ + n), n - 1)

sumTo(Thunk(0), depth).unsafePerformIO()
}

@Benchmark
def futureMap(): BigInt = {
import scala.concurrent.Future
import scala.concurrent.duration.Duration.Inf

@tailrec
def sumTo(t: Future[BigInt], n: Int): Future[BigInt] =
if (n <= 1) t
else sumTo(t.map(_ + n), n - 1)

Await.result(sumTo(Future(0), depth), Inf)
}

@Benchmark
def monixMap(): BigInt = {
import monix.eval.Task

@tailrec
def sumTo(t: Task[BigInt], n: Int): Task[BigInt] =
if (n <= 1) t
else sumTo(t.map(_ + n), n - 1)

sumTo(Task.eval(0), depth).runSyncMaybe.right.get
}

@Benchmark
def scalazMap(): BigInt = {
@tailrec
def sumTo(t: IO[BigInt], n: Int): IO[BigInt] =
if (n <= 1) t
else sumTo(t.map(_ + n), n - 1)

unsafePerformIO(sumTo(IO.point(0), depth))
}

@Benchmark
def catsMap(): BigInt = {
import cats.effect._

@tailrec
def sumTo(t: IO[BigInt], n: Int): IO[BigInt] =
if (n <= 1) t
else sumTo(t.map(_ + n), n - 1)

sumTo(IO(0), depth).unsafeRunSync()
}
}
Loading