Skip to content

Instantly share code, notes, and snippets.

@danicheg
Created December 22, 2024 10:53
Show Gist options
  • Select an option

  • Save danicheg/bb67fbf8373a15b049f7cea2d1266da1 to your computer and use it in GitHub Desktop.

Select an option

Save danicheg/bb67fbf8373a15b049f7cea2d1266da1 to your computer and use it in GitHub Desktop.
Redis4Cats / Zio-Redis microbenchmarking

I started with the current microbenchmarking suite from the zio-redis repository and tweaked it to check throughput, considering the different key values' sizes, from 128b to 32kb. I've been running microbenchmarking for the current versions of Redis4Cats and Zio-Redis:

"dev.profunktor" %% "redis4cats-effects" % "1.7.2"
"dev.zio" %% "zio-redis" % "1.0.0"

The suite's config:

@Measurement(iterations = 10)
@Warmup(iterations = 10)
@Fork(2)

Also, the machine was MacOS, M1 chip & 16Gb.

[info] Benchmark                   (bytes)  (parallelism)  (repetitions)   Mode  Cnt  Score   Error  Units
[info] SetGetBenchmark.Redis4Cats      128             32            128  thrpt   20  4.779 ± 0.176  ops/s
[info] SetGetBenchmark.Redis4Cats      512             32            128  thrpt   20  4.233 ± 0.102  ops/s
[info] SetGetBenchmark.Redis4Cats     2048             32            128  thrpt   20  3.516 ± 0.021  ops/s
[info] SetGetBenchmark.Redis4Cats     8192             32            128  thrpt   20  1.947 ± 0.059  ops/s
[info] SetGetBenchmark.Redis4Cats    32768             32            128  thrpt   20  0.806 ± 0.027  ops/s
[info] SetGetBenchmark.ZioRedis        128             32            128  thrpt   20  5.330 ± 0.099  ops/s
[info] SetGetBenchmark.ZioRedis        512             32            128  thrpt   20  3.947 ± 0.030  ops/s
[info] SetGetBenchmark.ZioRedis       2048             32            128  thrpt   20  2.135 ± 0.077  ops/s
[info] SetGetBenchmark.ZioRedis       8192             32            128  thrpt   20  0.656 ± 0.034  ops/s
[info] SetGetBenchmark.ZioRedis      32768             32            128  thrpt   20  0.177 ± 0.005  ops/s

The whole suite is listed in this gist.

package zio.redis.benchmarks
import cats.effect._
import cats.effect.unsafe.IORuntime
import dev.profunktor.redis4cats.connection._
import dev.profunktor.redis4cats.data.RedisCodec
import dev.profunktor.redis4cats.effect.Log.NoOp._
import dev.profunktor.redis4cats.{Redis, RedisCommands}
private[benchmarks] final class Redis4CatsClient private (
client: RedisCommands[IO, String, String],
finalizer: IO[Unit]
)(implicit runtime: IORuntime) {
def run(program: RedisCommands[IO, String, String] => IO[Unit]): Unit = program(client).unsafeRunSync()
def tearDown(): Unit = finalizer.unsafeRunSync()
}
private[benchmarks] object Redis4CatsClient {
implicit val runtime: IORuntime = IORuntime.global
def unsafeMake(): Redis4CatsClient = {
val resource = RedisClient[IO]
.from(RedisUri)
.flatMap(Redis[IO].fromClient(_, RedisCodec.Utf8))
val (client, finalizer) = extractResource(resource).unsafeRunSync()
new Redis4CatsClient(client, finalizer)
}
private def extractResource[A](resource: Resource[IO, A]): IO[(A, IO[Unit])] =
for {
da <- Deferred[IO, A]
f <- resource.use(da.complete(_) >> IO.never).start
a <- da.get
} yield a -> f.cancel
private final val RedisUri = "redis://127.0.0.1:6379"
}
package zio.redis.benchmarks
import cats.effect.IO
import cats.syntax.parallel._
import org.openjdk.jmh.annotations._
import zio.redis._
import zio.{Scope => _, _}
import java.util.UUID
import java.util.concurrent.TimeUnit
@State(Scope.Thread)
@BenchmarkMode(Array(Mode.Throughput))
@OutputTimeUnit(TimeUnit.SECONDS)
@Measurement(iterations = 10)
@Warmup(iterations = 10)
@Fork(2)
class SetGetBenchmark {
import SetGetBenchmark._
@Param(Array("32"))
var parallelism: Int = _
@Param(Array("128"))
var repetitions: Int = _
@Param(Array("128", "512", "2048", "8192", "32768"))
var bytes: Int = _
@Setup
def setup(): Unit = {
redis4CatsClient = Redis4CatsClient.unsafeMake()
zioRedisClient = ZioRedisClient.unsafeMake()
}
@TearDown
def tearDown(): Unit = {
redis4CatsClient.tearDown()
zioRedisClient.tearDown()
}
@TearDown(Level.Iteration)
def perIterationTearDown(): Unit =
redis4CatsClient.run(_.flushAll)
@Benchmark
def Redis4Cats(): Unit =
redis4CatsClient.run { client =>
val value = "F" * bytes
val task = IO(genTestKey).flatMap(key => client.set(key, value) *> client.get(key))
val tasks = List.fill(parallelism)(task.replicateA_(repetitions))
tasks.parSequence_
}
@Benchmark
def ZioRedis(): Unit =
zioRedisClient.run {
for {
redis <- ZIO.service[Redis]
value = "F" * bytes
task =
ZIO.attempt(genTestKey).flatMap(key => redis.set(key, value) *> redis.get(key).returning[String]).orDie
tasks = Chunk.fill(parallelism)(task.repeatN(repetitions))
_ <- ZIO.collectAllParDiscard(tasks)
} yield ()
}
private var redis4CatsClient: Redis4CatsClient = _
private var zioRedisClient: ZioRedisClient = _
}
object SetGetBenchmark {
private def genTestKey = UUID.randomUUID().toString
}
package zio.redis.benchmarks
import zio._
import zio.redis._
import zio.redis.internal.Utf8Codec
import zio.schema._
import zio.schema.codec.BinaryCodec
private[benchmarks] final class ZioRedisClient private (runtime: Runtime.Scoped[Redis]) {
def run(program: ZIO[Redis, RedisError, Unit]): Unit =
Unsafe.unsafe(implicit u => runtime.unsafe.run(program).getOrThrow())
def tearDown(): Unit =
Unsafe.unsafe(implicit u => runtime.unsafe.shutdown())
}
private[benchmarks] object ZioRedisClient {
def unsafeMake(): ZioRedisClient = {
val runtime = Unsafe.unsafe(implicit u => Runtime.unsafe.fromLayer(TestLayer))
new ZioRedisClient(runtime)
}
private object ProtobufCodecSupplier extends CodecSupplier {
def get[A: Schema]: BinaryCodec[A] = Utf8Codec.codec
}
private final val TestLayer =
ZLayer.make[Redis](Redis.local, ZLayer.succeed(ProtobufCodecSupplier))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment