Skip to content

Instantly share code, notes, and snippets.

Understanding Comparative Benchmarks

I'm going to do something that I don't normally do, which is to say I'm going to talk about comparative benchmarks. In general, I try to confine performance discussion to absolute metrics as much as possible, or comparisons to other well-defined neutral reference points. This is precisely why Cats Effect's readme mentions a comparison to a fixed thread pool, rather doing comparisons with other asynchronous runtimes like Akka or ZIO. Comparisons in general devolve very quickly into emotional marketing.

But, just once, today we're going to talk about the emotional marketing. In particular, we're going to look at Cats Effect 3 and ZIO 2. Now, for context, as of this writing ZIO 2 has released their first milestone; they have not released a final 2.0 version. This implies straight off the bat that we're comparing apples to oranges a bit, since Cats Effect 3 has been out and in production for months. However, there has been a post going around which cites various compar

@bpholt
bpholt / generate-publishing-keypair.sh
Last active May 19, 2023 16:50
generate a GPG key pair for signing artifacts published by sbt, save it to a 1Password vault, and set the necessary secrets for GitHub Actions
#!/usr/bin/env bash
set -o errexit -o nounset -o pipefail
IFS=$'\n\t'
RED='\033[1;31m'
NC='\033[0m' # No Color
readonly RED NC
function log_error () {
echo -e "$1" > /dev/stderr
}
@OlegYch
OlegYch / build.sbt
Last active July 18, 2023 14:50
get compile times by file/dir in sbt
val compileTimes = inputKey[Unit]("Get compilation time for each folder")
compileTimes / aggregate := false,
compileTimes := {
import complete.DefaultParsers._
// val ((depth, _), filter) = (NatBasic ~ " " ~ StringBasic).parsed
val (depth, filter) = spaceDelimited("depth filter").parsed match {
case Seq(depth, filter) => (depth.toInt, filter)
case _ => sys.error("expected depth and filter arguments, eg '2 /'")
}
val s = state.value
@gatorcse
gatorcse / StreamMerger.scala
Last active January 14, 2023 21:09
Merging two already sorted fs2 streams, with a sortBy function.
import cats._
import cats.implicits._
import cats.effect._
import cats.effect.implicits._
import fs2._
class StreamMerger[F[_]] {
def priorityOrderBy[A, B: Order](s1: Stream[F, A], s2: Stream[F, A])(f: A => B): Stream[F, A] = {
def go(p1: Stream.StepLeg[F, A], p2: Stream.StepLeg[F, A]): Pull[F, A, Unit] = {
@mpilquist
mpilquist / predef.scala
Last active August 26, 2021 22:15
Ammonite REPL predef for use with fs2
// Save as ~/.ammonite/predef.sc
// To use fs2 from ammonite repl, type `load.fs2` from repl prompt.
// You'll get all fs2 & cats imports, ContextShift and Timer instances
// for IO, and a globalBlocker
import $plugin.$ivy.`org.typelevel:::kind-projector:0.11.0`
if (!repl.compiler.settings.isScala213)
repl.load.apply("interp.configureCompiler(_.settings.YpartialUnification.value = true)")
@gvolpe
gvolpe / parTraverseN.scala
Last active February 15, 2024 15:29
parTraverse with a limit of N using a Semaphore
import cats.Traverse
import cats.effect._
import cats.effect.concurrent.Semaphore
import cats.temp.par._
import cats.syntax.all._
import scala.concurrent.duration._
object Main extends IOApp {
import ParTask._
@kiambogo
kiambogo / groupBy.scala
Last active September 22, 2020 00:01
FS2 1.0.0 groupBy
import fs2.concurrent.Queue
import cats.implicits._
import cats.effect.Concurrent
import cats.effect.concurrent.Ref
def groupBy[F[_], A, K](selector: A => F[K])(implicit F: Concurrent[F]): Pipe[F, A, (K, Stream[F, A])] = {
in =>
Stream.eval(Ref.of[F, Map[K, Queue[F, Option[A]]]](Map.empty)).flatMap { st =>
val cleanup = {
import alleycats.std.all._
@SystemFw
SystemFw / groupBy.scala
Created July 9, 2018 10:32
fs2 `groupBy/partitions`
// Grows with the number of distinct `K`
def partitions[F[_], A, K](selector: A => F[K])(implicit F: Effect[F], ec: ExecutionContext) : Pipe[F, A, (K, Stream[F, A])] = in =>
Stream.eval(async.refOf[F, Map[K, Queue[F, Option[A]]]](Map.empty)).flatMap { st =>
val cleanup = {
import alleycats.std.all._
st.get.flatMap(_.traverse_(_.enqueue1(None)))
}
(in ++ Stream.eval_(cleanup)).evalMap { el =>
(selector(el), st.get).mapN { (key, queues) =>
@filosganga
filosganga / GuavaFutures.scala
Created September 16, 2017 06:25
How to convert a Guava ListeanbelFuture to Cats Async
import cats.effect.Async
import com.google.common.util.concurrent.{FutureCallback, Futures, ListenableFuture}
import scala.concurrent.ExecutionContext
object GuavaFutures {
implicit class RichListenableFuture[T](lf: ListenableFuture[T]) {
def toAsync[F[_]: Async](implicit ec: ExecutionContext): F[T] = {

Thread Pools

Thread pools on the JVM should usually be divided into the following three categories:

  1. CPU-bound
  2. Blocking IO
  3. Non-blocking IO polling

Each of these categories has a different optimal configuration and usage pattern.