Skip to content

Commit

Permalink
Merge pull request #519 from non/topic/eval-call-bench
Browse files Browse the repository at this point in the history
Set up a benchmark for our trampolines.
  • Loading branch information
adelbertc committed Sep 15, 2015
2 parents c63e0a5 + b4a9df2 commit 14b5772
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 6 deletions.
6 changes: 4 additions & 2 deletions bench/src/main/scala/cats/bench/FoldBench.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ class FoldBench {
val chars: List[String] = ('a' to 'z').map(_.toString).toList

/** Benchmark fold of Foldable[List] */
@Benchmark def fold(): String =
@Benchmark
def fold(): String =
Foldable[List].fold(chars)

/** Benchmark fold using traverse with Const */
@Benchmark def traverseConst(): String =
@Benchmark
def traverseConst(): String =
Traverse[List].traverse[Const[String, ?], String, String](chars)(Const(_)).getConst

}
45 changes: 45 additions & 0 deletions bench/src/main/scala/cats/bench/TrampolineBench.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package cats.bench

import org.openjdk.jmh.annotations.{Benchmark, Scope, State}

import cats._
import cats.implicits._
import cats.free.Trampoline

import scala.util.control.TailCalls

@State(Scope.Benchmark)
class TrampolineBench {

val N = 15

@Benchmark
def eval(): Int = evalFib(N).value

def evalFib(n: Int): Eval[Int] =
if (n < 2) Eval.now(n) else for {
x <- Eval.defer(evalFib(n - 1))
y <- Eval.defer(evalFib(n - 2))
} yield x + y


@Benchmark
def trampoline(): Int = trampolineFib(N).run

def trampolineFib(n: Int): Trampoline[Int] =
if (n < 2) Trampoline.done(n) else for {
x <- Trampoline.suspend(trampolineFib(n - 1))
y <- Trampoline.suspend(trampolineFib(n - 2))
} yield x + y

// TailRec[A] only has .flatMap in 2.11.

// @Benchmark
// def stdlib(): Int = stdlibFib(N).result
//
// def stdlibFib(n: Int): TailCalls.TailRec[Int] =
// if (n < 2) TailCalls.done(n) else for {
// x <- TailCalls.tailcall(stdlibFib(n - 1))
// y <- TailCalls.tailcall(stdlibFib(n - 2))
// } yield x + y
}
3 changes: 1 addition & 2 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import com.typesafe.sbt.pgp.PgpKeys.publishSigned
import com.typesafe.sbt.SbtSite.SiteKeys._
import com.typesafe.sbt.SbtGhPages.GhPagesKeys._
import pl.project13.scala.sbt.SbtJmh._
import sbtunidoc.Plugin.UnidocKeys._
import ReleaseTransformations._
import ScoverageSbtPlugin._
Expand Down Expand Up @@ -189,8 +188,8 @@ lazy val bench = project.dependsOn(macrosJVM, coreJVM, freeJVM, lawsJVM)
.settings(moduleName := "cats-bench")
.settings(catsSettings)
.settings(noPublishSettings)
.settings(jmhSettings)
.settings(commonJvmSettings)
.enablePlugins(JmhPlugin)

// cats-js is JS-only
lazy val js = project
Expand Down
20 changes: 19 additions & 1 deletion core/src/main/scala/cats/Eval.scala
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ sealed abstract class Eval[A] { self =>
val run = f
}
}
case c: Eval.Call[A] =>
new Eval.Compute[B] {
type Start = A
val start = c.thunk
val run = f
}
case _ =>
new Eval.Compute[B] {
type Start = A
Expand Down Expand Up @@ -194,7 +200,7 @@ object Eval extends EvalInstances {
* which produces an Eval[A] value. Like .flatMap, it is stack-safe.
*/
def defer[A](a: => Eval[A]): Eval[A] =
Eval.Unit.flatMap(_ => a)
new Eval.Call[A](a _) {}

/**
* Static Eval instances for some common values.
Expand All @@ -208,6 +214,18 @@ object Eval extends EvalInstances {
val Zero: Eval[Int] = Now(0)
val One: Eval[Int] = Now(1)

/**
* Call is a type of Eval[A] that is used to defer computations
* which produce Eval[A].
*
* Users should not instantiate Call instances themselves. Instead,
* they will be automatically created when needed.
*/
sealed abstract class Call[A](val thunk: () => Eval[A]) extends Eval[A] {
def memoize: Eval[A] = new Later(() => thunk().value)
def value: A = thunk().value
}

/**
* Compute is a type of Eval[A] that is used to chain computations
* involving .map and .flatMap. Along with Eval#flatMap it
Expand Down
2 changes: 1 addition & 1 deletion project/plugins.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.0.0")
addSbtPlugin("com.typesafe.sbt" % "sbt-ghpages" % "0.5.3")
addSbtPlugin("com.typesafe.sbt" % "sbt-site" % "0.8.1")
addSbtPlugin("org.tpolecat" % "tut-plugin" % "0.4.0")
addSbtPlugin("pl.project13.scala"% "sbt-jmh" % "0.1.10")
addSbtPlugin("pl.project13.scala"% "sbt-jmh" % "0.2.3")
addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "0.6.0")
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.2.0")
addSbtPlugin("com.typesafe.sbt" % "sbt-git" % "0.8.4")
Expand Down

0 comments on commit 14b5772

Please sign in to comment.