Skip to content

Commit 2f540d6

Browse files
authored
Add benchmarks for IO (#1441)
1 parent 9e00f04 commit 2f540d6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1255
-0
lines changed

build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ buildscript {
5555
classpath 'org.ajoberstar:gradle-git-publish:2.0.0'
5656
classpath "net.rdrei.android.buildtimetracker:gradle-plugin:0.11.1"
5757
classpath "org.jetbrains.dokka:dokka-gradle-plugin:$dokka_version"
58+
classpath "me.champeau.gradle:jmh-gradle-plugin:0.4.7"
59+
classpath "gradle.plugin.io.morethan.jmhreport:gradle-jmh-report:0.9.0"
5860
classpath "org.jlleitschuh.gradle:ktlint-gradle:$ktlint_version"
5961
}
6062
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
dependencies {
2+
compile project(":arrow-effects-data")
3+
compile project(":arrow-effects-extensions")
4+
compile project(":arrow-effects-io-extensions")
5+
compile project(":arrow-effects-rx2-extensions")
6+
compile project(":arrow-effects-reactor-extensions")
7+
compile project(":arrow-scala-benchmarks")
8+
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion"
9+
compile "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinxCoroutinesVersion"
10+
compileOnly "org.openjdk.jmh:jmh-core:1.21"
11+
}
12+
13+
apply plugin: "me.champeau.gradle.jmh"
14+
apply plugin: "io.morethan.jmhreport"
15+
16+
jmh {
17+
include = [
18+
'arrow.benchmarks.Async',
19+
'arrow.benchmarks.AttemptNonRaised',
20+
'arrow.benchmarks.AttemptRaisedError',
21+
// 'arrow.benchmarks.Bracket',
22+
// 'arrow.benchmarks.Cancellable',
23+
'arrow.benchmarks.DeepBind',
24+
// 'arrow.benchmarks.Defer',
25+
'arrow.benchmarks.Delay',
26+
// 'arrow.benchmarks.ForkFiber',
27+
// 'arrow.benchmarks.HandleNonRaised',
28+
// 'arrow.benchmarks.HandleRaisedError',
29+
'arrow.benchmarks.LeftBind',
30+
'arrow.benchmarks.Map',
31+
'arrow.benchmarks.MapStream',
32+
// 'arrow.benchmarks.ParMap',
33+
'arrow.benchmarks.Pure',
34+
// 'arrow.benchmarks.RacePair',
35+
// 'arrow.benchmarks.Uncancellable'
36+
]
37+
resultFormat = 'json'
38+
resultsFile = file('build/reports/benchmarks.json')
39+
timeOnIteration = '1s'
40+
failOnError = true
41+
if (project.hasProperty("jmhInclude"))
42+
include = [jmhInclude]
43+
if (project.hasProperty("jmhResultsFile"))
44+
resultsFile = file(jmhResultsFile)
45+
46+
}
47+
48+
jmhReport {
49+
jmhResultPath = project.file('build/reports/benchmarks.json')
50+
jmhReportOutput = project.file('build/reports')
51+
}
52+
53+
tasks.jmh.finalizedBy tasks.jmhReport
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Maven publishing configuration
2+
POM_NAME=Arrow-Benchmarks-Effects
3+
POM_ARTIFACT_ID=arrow-benchmarks-effects
4+
POM_PACKAGING=jar
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package arrow.benchmarks
2+
3+
import arrow.effects.IO
4+
import arrow.effects.IODispatchers
5+
import org.openjdk.jmh.annotations.Benchmark
6+
import org.openjdk.jmh.annotations.CompilerControl
7+
import org.openjdk.jmh.annotations.Fork
8+
import org.openjdk.jmh.annotations.Measurement
9+
import org.openjdk.jmh.annotations.Param
10+
import org.openjdk.jmh.annotations.Scope
11+
import org.openjdk.jmh.annotations.State
12+
import org.openjdk.jmh.annotations.Warmup
13+
import java.util.concurrent.TimeUnit
14+
15+
@State(Scope.Thread)
16+
@Fork(2)
17+
@Warmup(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS)
18+
@Measurement(iterations = 10)
19+
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
20+
open class Async {
21+
22+
@Param("3000")
23+
var size: Int = 0
24+
25+
private fun ioAsyncLoop(i: Int): IO<Int> =
26+
IO.unit.continueOn(IODispatchers.CommonPool).followedBy(
27+
if (i > size) IO.just(i) else ioAsyncLoop(i + 1)
28+
)
29+
30+
@Benchmark
31+
fun io(): Int =
32+
ioAsyncLoop(0).unsafeRunSync()
33+
34+
@Benchmark
35+
fun catsIO(): Int =
36+
arrow.benchmarks.effects.scala.cats.`Async$`.`MODULE$`.unsafeIOAsyncLoop(size, 0)
37+
38+
@Benchmark
39+
fun scalazZIO(): Int =
40+
arrow.benchmarks.effects.scala.zio.`Async$`.`MODULE$`.unsafeIOAsyncLoop(size, 0)
41+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package arrow.benchmarks
2+
3+
import arrow.effects.IO
4+
import org.openjdk.jmh.annotations.Benchmark
5+
import org.openjdk.jmh.annotations.CompilerControl
6+
import org.openjdk.jmh.annotations.Fork
7+
import org.openjdk.jmh.annotations.Measurement
8+
import org.openjdk.jmh.annotations.Param
9+
import org.openjdk.jmh.annotations.Scope
10+
import org.openjdk.jmh.annotations.State
11+
import org.openjdk.jmh.annotations.Warmup
12+
import java.util.concurrent.TimeUnit
13+
14+
@State(Scope.Thread)
15+
@Fork(2)
16+
@Warmup(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS)
17+
@Measurement(iterations = 10)
18+
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
19+
open class AttemptNonRaised {
20+
21+
@Param("10000")
22+
var size: Int = 0
23+
24+
private fun ioLoopHappy(size: Int, i: Int): IO<Int> =
25+
if (i < size) {
26+
IO { i + 1 }.attempt().flatMap {
27+
it.fold(IO.Companion::raiseError) { n -> ioLoopHappy(size, n) }
28+
}
29+
} else IO.just(1)
30+
31+
@Benchmark
32+
fun io(): Int =
33+
ioLoopHappy(size, 0).unsafeRunSync()
34+
35+
@Benchmark
36+
fun cats(): Any =
37+
arrow.benchmarks.effects.scala.cats.AttemptNonRaised.ioLoopHappy(size, 0).unsafeRunSync()
38+
39+
@Benchmark
40+
fun zio(): Any =
41+
arrow.benchmarks.effects.scala.zio.AttemptNonRaised.run(size)
42+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package arrow.benchmarks
2+
3+
import arrow.effects.IO
4+
import org.openjdk.jmh.annotations.Benchmark
5+
import org.openjdk.jmh.annotations.CompilerControl
6+
import org.openjdk.jmh.annotations.Fork
7+
import org.openjdk.jmh.annotations.Measurement
8+
import org.openjdk.jmh.annotations.Param
9+
import org.openjdk.jmh.annotations.Scope
10+
import org.openjdk.jmh.annotations.State
11+
import org.openjdk.jmh.annotations.Warmup
12+
import java.util.concurrent.TimeUnit
13+
14+
val dummy = object : RuntimeException("dummy") {
15+
override fun fillInStackTrace(): Throwable =
16+
this
17+
}
18+
19+
@State(Scope.Thread)
20+
@Fork(2)
21+
@Warmup(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS)
22+
@Measurement(iterations = 10)
23+
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
24+
open class AttemptRaisedError {
25+
26+
@Param("10000")
27+
var size: Int = 0
28+
29+
private fun ioLoopNotHappy(size: Int, i: Int): IO<Int> =
30+
if (i < size) {
31+
IO { throw dummy }.attempt().flatMap {
32+
it.fold({ ioLoopNotHappy(size, i + 1) }, IO.Companion::just)
33+
}
34+
} else IO.just(1)
35+
36+
@Benchmark
37+
fun io(): Int =
38+
ioLoopNotHappy(size, 0).unsafeRunSync()
39+
40+
@Benchmark
41+
fun cats(): Any =
42+
arrow.benchmarks.effects.scala.cats.AttemptRaisedError.ioLoopNotHappy(size, 0).unsafeRunSync()
43+
44+
@Benchmark
45+
fun zio(): Any =
46+
arrow.benchmarks.effects.scala.zio.AttemptRaisedError.run(size)
47+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package arrow.benchmarks
2+
3+
import arrow.effects.IO
4+
import arrow.unsafe
5+
import org.openjdk.jmh.annotations.Benchmark
6+
import org.openjdk.jmh.annotations.CompilerControl
7+
import org.openjdk.jmh.annotations.Fork
8+
import org.openjdk.jmh.annotations.Measurement
9+
import org.openjdk.jmh.annotations.Param
10+
import org.openjdk.jmh.annotations.Scope
11+
import org.openjdk.jmh.annotations.State
12+
import org.openjdk.jmh.annotations.Warmup
13+
import java.util.concurrent.TimeUnit
14+
import arrow.effects.extensions.io.unsafeRun.runBlocking as ioRunBlocking
15+
16+
@State(Scope.Thread)
17+
@Fork(2)
18+
@Warmup(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS)
19+
@Measurement(iterations = 10)
20+
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
21+
open class Bracket {
22+
23+
@Param("100")
24+
var size: Int = 0
25+
26+
private fun ioBracketLoop(i: Int): IO<Int> =
27+
if (i < size)
28+
IO.just(i).bracket({ IO.unit }, { ib -> IO { ib + 1 } }).flatMap { ioBracketLoop(it) }
29+
else
30+
IO.just(i)
31+
32+
@Benchmark
33+
fun io() =
34+
unsafe {
35+
ioRunBlocking {
36+
ioBracketLoop(0)
37+
}
38+
}
39+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package arrow.benchmarks
2+
3+
import arrow.core.Right
4+
import arrow.effects.IO
5+
import arrow.effects.extensions.io.concurrent.concurrent
6+
import arrow.effects.fix
7+
import org.openjdk.jmh.annotations.Benchmark
8+
import org.openjdk.jmh.annotations.CompilerControl
9+
import org.openjdk.jmh.annotations.Fork
10+
import org.openjdk.jmh.annotations.Measurement
11+
import org.openjdk.jmh.annotations.Param
12+
import org.openjdk.jmh.annotations.Scope
13+
import org.openjdk.jmh.annotations.State
14+
import org.openjdk.jmh.annotations.Warmup
15+
import java.util.concurrent.TimeUnit
16+
17+
@State(Scope.Thread)
18+
@Fork(2)
19+
@Warmup(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS)
20+
@Measurement(iterations = 10)
21+
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
22+
open class Cancellable {
23+
24+
@Param("100")
25+
var size: Int = 0
26+
27+
fun evalCancelable(n: Int): IO<Int> =
28+
IO.concurrent().cancelable<Int> { cb ->
29+
cb(Right(n))
30+
IO.unit
31+
}.fix()
32+
33+
fun cancelableLoop(i: Int): IO<Int> =
34+
if (i < size) evalCancelable(i + 1).flatMap { cancelableLoop(it) }
35+
else evalCancelable(i)
36+
37+
@Benchmark
38+
fun io(): Int =
39+
cancelableLoop(0).unsafeRunSync()
40+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package arrow.benchmarks
2+
3+
import arrow.effects.IO
4+
import org.openjdk.jmh.annotations.Benchmark
5+
import org.openjdk.jmh.annotations.CompilerControl
6+
import org.openjdk.jmh.annotations.Fork
7+
import org.openjdk.jmh.annotations.Measurement
8+
import org.openjdk.jmh.annotations.Param
9+
import org.openjdk.jmh.annotations.Scope
10+
import org.openjdk.jmh.annotations.State
11+
import org.openjdk.jmh.annotations.Warmup
12+
import java.util.concurrent.TimeUnit
13+
14+
@State(Scope.Thread)
15+
@Fork(2)
16+
@Warmup(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS)
17+
@Measurement(iterations = 10)
18+
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
19+
open class DeepBind {
20+
21+
@Param("20")
22+
var depth: Int = 0
23+
24+
fun ioFibLazy(n: Int): IO<Int> =
25+
if (n <= 1) IO { n }
26+
else ioFibLazy(n - 1).flatMap { a ->
27+
ioFibLazy(n - 2).flatMap { b -> IO { a + b } }
28+
}
29+
30+
@Benchmark
31+
fun io(): Int =
32+
ioFibLazy(depth).unsafeRunSync()
33+
34+
@Benchmark
35+
fun cats(): Any =
36+
arrow.benchmarks.effects.scala.cats.`DeepBind$`.`MODULE$`.fib(depth).unsafeRunSync()
37+
38+
@Benchmark
39+
fun zio(): Any =
40+
arrow.benchmarks.effects.scala.zio.`DeepBind$`.`MODULE$`.fib(depth)
41+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package arrow.benchmarks
2+
3+
import arrow.effects.IO
4+
import org.openjdk.jmh.annotations.Benchmark
5+
import org.openjdk.jmh.annotations.CompilerControl
6+
import org.openjdk.jmh.annotations.Fork
7+
import org.openjdk.jmh.annotations.Measurement
8+
import org.openjdk.jmh.annotations.Param
9+
import org.openjdk.jmh.annotations.Scope
10+
import org.openjdk.jmh.annotations.State
11+
import org.openjdk.jmh.annotations.Warmup
12+
import java.util.concurrent.TimeUnit
13+
14+
@State(Scope.Thread)
15+
@Fork(2)
16+
@Warmup(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS)
17+
@Measurement(iterations = 10)
18+
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
19+
open class Defer {
20+
21+
@Param("3000")
22+
var size: Int = 0
23+
24+
private fun ioDeferLoop(i: Int): IO<Int> =
25+
IO.defer { IO.just(i) }.flatMap { j ->
26+
if (j > size) IO.defer { IO.just(j) } else ioDeferLoop(j + 1)
27+
}
28+
29+
@Benchmark
30+
fun io(): Int =
31+
ioDeferLoop(0).unsafeRunSync()
32+
}

0 commit comments

Comments
 (0)