/* * Copyright (c) 2017-2019 The Typelevel Cats-effect Project Developers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package cats.effect.benchmarks import java.util.concurrent.TimeUnit import cats.effect.IO import org.openjdk.jmh.annotations._ /** To do comparative benchmarks between versions: * * benchmarks/run-benchmark MapStreamBenchmark * * This will generate results in `benchmarks/results`. * * Or to run the benchmark from within sbt: * * jmh:run -i 10 -wi 10 -f 2 -t 1 cats.effect.benchmarks.MapStreamBenchmark * * Which means "10 iterations", "10 warm-up iterations", "2 forks", "1 thread". * Please note that benchmarks should be usually executed at least in * 10 iterations (as a rule of thumb), but more is better. */ @State(Scope.Thread) @BenchmarkMode(Array(Mode.Throughput)) @OutputTimeUnit(TimeUnit.SECONDS) class MapStreamBenchmark { import MapStreamBenchmark.streamTest @Benchmark def one(): Long = streamTest(12000, 1) @Benchmark def batch30(): Long = streamTest(1000, 30) @Benchmark def batch120(): Long = streamTest(100, 120) } object MapStreamBenchmark { def streamTest(times: Int, batchSize: Int): Long = { var stream = range(0, times) var i = 0 while (i < batchSize) { stream = mapStream(addOne)(stream) i += 1 } sum(0)(stream).unsafeRunSync() } final case class Stream(value: Int, next: IO[Option[Stream]]) val addOne = (x: Int) => x + 1 def range(from: Int, until: Int): Option[Stream] = if (from < until) Some(Stream(from, IO(range(from + 1, until)))) else None def mapStream(f: Int => Int)(box: Option[Stream]): Option[Stream] = box match { case Some(Stream(value, next)) => Some(Stream(f(value), next.map(mapStream(f)))) case None => None } def sum(acc: Long)(box: Option[Stream]): IO[Long] = box match { case Some(Stream(value, next)) => next.flatMap(sum(acc + value)) case None => IO.pure(acc) } }