Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
6de0c39
Reduce allocations for JDocument creation
ronanM Jul 31, 2022
fdd1079
Remove the allocations of scala.collection.convert.AsJavaExtensions
ronanM Jul 31, 2022
bfe5eb5
Add multiple implementations of `mongo4cats.AsJava`
ronanM Jul 31, 2022
5242d32
Fix 2.12 compile
ronanM Aug 2, 2022
9147d7a
$ sbt githubWorkflowGenerate
ronanM Aug 2, 2022
7201cc1
Merge upstream
ronanM Aug 2, 2022
ec129e0
[WIP] Add generic derivation to org.bson.BsonValue
ronanM Aug 17, 2022
f054bcf
[WIP] Add generic derivation to org.bson.BsonValue
ronanM Aug 17, 2022
90c1032
Merge upstream/master
ronanM Aug 17, 2022
7a4890d
Add MongoBsonCollectionSpec
ronanM Aug 17, 2022
4a12254
Add implicit `MongoCodecProvider[Fast[A]]`
ronanM Aug 18, 2022
0539a01
Bench
ronanM Aug 19, 2022
5cd8dfb
Fix discriminator
ronanM Aug 19, 2022
af5739e
Fix compile
ronanM Aug 19, 2022
250aa33
Add DerivationReadBench
ronanM Aug 20, 2022
2fdc899
WIP optimizations in BsonDecoder
ronanM Aug 22, 2022
76c2276
Cleanup
ronanM Aug 23, 2022
eaf5f01
Faster MagnoliaBsonEncoder
ronanM Aug 26, 2022
536e0c6
Trying to read faster
ronanM Aug 30, 2022
ff21075
Tuples BsonEncoder and BsonDecoder
ronanM Aug 31, 2022
e6c1565
Merge master
ronanM Aug 31, 2022
e039ad7
Add BsonEncoderInstanceBench, BsonDecoderInstanceBench & BsonDecoderTest
ronanM Sep 3, 2022
cc24421
Add Either & Validated
ronanM Sep 6, 2022
022632b
Add CollectionReadBench & CollectionWriteBench
ronanM Sep 18, 2022
8e6b4bb
Merge master
ronanM Sep 19, 2022
d157027
Cleanup
ronanM Sep 21, 2022
5620039
Remove yoloMode
ronanM Sep 22, 2022
69d0a95
More data diversity in benches
ronanM Sep 25, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 142 additions & 0 deletions bench/src/main/scala-2/mongo4cats/bench/BaseCollectionBench.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package mongo4cats.bench

import cats.syntax.all._
import cats.effect.IO
import de.flapdoodle.embed.mongo.config.Defaults.{downloadConfigFor, extractedArtifactStoreFor, runtimeConfigFor}
import de.flapdoodle.embed.mongo.config.{MongodConfig, Net, Storage}
import de.flapdoodle.embed.mongo.distribution.Version
import de.flapdoodle.embed.mongo.packageresolver.Command.MongoD
import de.flapdoodle.embed.mongo.{MongodProcess, MongodStarter}
import de.flapdoodle.embed.process.extract.{DirectoryAndExecutableNaming, NoopTempNaming, UUIDTempNaming, UserTempNaming}
import de.flapdoodle.embed.process.io.directories.FixedPath
import de.flapdoodle.embed.process.runtime.Network
import io.circe.generic.auto._
import mongo4cats.bench.BenchData.data
import mongo4cats.circe._
import mongo4cats.client.MongoClient
import mongo4cats.collection.GenericMongoCollection
import mongo4cats.database.GenericMongoDatabase
import mongo4cats.derivation.bson.{BsonDecoder, BsonEncoder, _}
import org.openjdk.jmh.annotations.TearDown
import org.slf4j.LoggerFactory
import org.slf4j.helpers.NOPLogger.NOP_LOGGER

import java.io.File
import java.nio.file.{Files, Path, Paths}
import java.time.Instant
import java.util.Comparator
import java.util.Comparator.reverseOrder
import scala.sys.process._

trait BaseCollectionBench { self =>

val mongoPort = 57057

var mongoProcess: MongodProcess = _
var client: MongoClient[IO] = _
var releaseClient: IO[Unit] = _
var db: GenericMongoDatabase[IO, fs2.Stream[IO, *]] = _
var circeColl: GenericMongoCollection[IO, BenchData, fs2.Stream[IO, *]] = _
var bsonColl: GenericMongoCollection[IO, BenchData, fs2.Stream[IO, *]] = _

val benchDB = "bench-db"
val benchColl = "bench-coll"
val tmpDir = "/tmp/big/mongo4cats.tmp"
val baseDataDir = s"$tmpDir/data"
val shmDir = "/dev/shm/mongo4cats"

def deleteDir(dirPath: String): IO[Unit] = {
val dir = new File(dirPath)
IO.blocking(if (dir.exists()) Files.walk(dir.toPath).sorted(reverseOrder[Path]()).map(_.toFile).forEach { f => f.delete(); () })
}

def setup(): Unit = {
implicit val bsonEncoder: BsonEncoder[BenchData] = DerivationWriteBench.bsonEncoder
implicit val bsonDecoder: BsonDecoder[BenchData] = DerivationReadBench.bsonDecoder

val dataDir = s"$baseDataDir/${Instant.now}"
// val instanceDir = s"$shmDirFile/data/${Instant.now}"

(for {
_ <- deleteDir(shmDir)

_mongoProcess <- IO
.blocking(
MongodStarter
.getInstance(
runtimeConfigFor(
MongoD,
// LoggerFactory.getLogger(classOf[BaseCollectionBench])
NOP_LOGGER
)
.artifactStore(
extractedArtifactStoreFor(MongoD)
.withDownloadConfig(
downloadConfigFor(MongoD)
.artifactStorePath(new FixedPath(s"$tmpDir/downloadedMongoArchive"))
.build()
)
.withExtraction(
DirectoryAndExecutableNaming
.builder()
.directory(new FixedPath(s"$tmpDir/extracted"))
.executableNaming(new NoopTempNaming())
.build()
)
.withTemp(
DirectoryAndExecutableNaming
.builder()
.directory(new FixedPath(s"$tmpDir/exec"))
.executableNaming(new NoopTempNaming())
.build()
)
)
// .processOutput(new ProcessOutput(processOutput("OUT", FULL_MONGOD_LOG), processOutput("ERROR"), processOutput("CMD")))
.build()
)
.prepare(
MongodConfig.builder
.version(Version.Main.PRODUCTION)
.net(new Net("localhost", mongoPort, Network.localhostIsIPv6))
.replication(new Storage(dataDir, null, 0 /*MegaByte*/ ))
.build
)
.start()
)
.attemptTap {
case Left(ex) =>
IO(ex.printStackTrace()) *>
IO(Seq("pkill", "-9", "-f", "extractmongod").!)
case Right(value) => IO.unit
}

_ = self.mongoProcess = _mongoProcess

client_releaseClient <- MongoClient.fromConnectionString[IO](s"mongodb://localhost:$mongoPort").allocated
_ = self.client = client_releaseClient._1
_ = self.releaseClient = client_releaseClient._2

// --- Circe ---
db <- client.getDatabase(benchDB)
_ = self.db = db
// _ = println(s"""|Write Concern: ${db.writeConcern}
// |Read Concern: ${db.readConcern}
// |""".stripMargin)

_ <- db.createCollection(benchColl)
circeColl <- db.getCollectionWithCodec[BenchData](benchColl)
_ = self.circeColl = circeColl

// --- BsonValue ---
bsonColl <- db.fastCollection[BenchData](benchColl)
_ = self.bsonColl = bsonColl
} yield ()).unsafeRunSync()(cats.effect.unsafe.IORuntime.global)
}

@TearDown
def tearDown(): Unit = (for {
_ <- releaseClient
_ <- IO.blocking(mongoProcess.stop())
_ <- deleteDir(baseDataDir)
} yield ()).unsafeRunSync()(cats.effect.unsafe.IORuntime.global)
}
60 changes: 60 additions & 0 deletions bench/src/main/scala-2/mongo4cats/bench/BenchData.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package mongo4cats.bench

import cats.data.Validated
import cats.syntax.all._
import org.scalacheck.{Arbitrary, Gen}
import org.scalacheck.ScalacheckShapeless._
import org.scalacheck.cats.implicits._

import java.time.Instant
import java.util.UUID

trait BaseGen {
implicit val instantArb: Arbitrary[Instant] =
Arbitrary(Gen.choose(0, 1000000L).map(Instant.ofEpochSecond))

implicit val objectIdArb: Arbitrary[org.bson.types.ObjectId] =
Arbitrary((Gen.choose(0, 16777215), Gen.choose(0, 16777215)).mapN(new org.bson.types.ObjectId(_, _)))

implicit val shortStringArb: Arbitrary[String] =
Arbitrary(Gen.choose(0, 15) >>= (Gen.stringOfN(_, Gen.alphaNumChar)))
}

final case class BenchData(items: List[Items])

object BenchData extends BaseGen {
val data: BenchData = Gen.listOfN(30_000, Arbitrary.arbitrary[Items]).map(BenchData(_)).sample.get
}

final case class Items(
st: ItemST,
s: String,
c: Char,
b: Byte,
short: Short,
i: Int,
l: Long,
id: UUID,
time: Instant
)

sealed trait ItemST

object ItemST {
final case object ItemCObj extends ItemST

final case class ItemST1(
sh: Short,
i: Int,
b: Byte,
map: Map[Int, Long]
) extends ItemST

final case class ItemST2(
s: String,
intOpt: Option[Int],
int: Int,
//either: Either[Long, String],
//validated: Validated[Int, Char]
) extends ItemST
}
60 changes: 60 additions & 0 deletions bench/src/main/scala-2/mongo4cats/bench/BenchTenMyCC.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package mongo4cats.bench

import cats.syntax.all._
import mongo4cats.bench.ItemST.ItemST2
import mongo4cats.derivation.bson.{BsonDecoder, BsonEncoder, JavaEncoder}
import org.bson.{BsonDocument, BsonString, BsonWriter}
import org.bson.codecs.EncoderContext

import java.time.Instant
import java.time.temporal.ChronoUnit

final case class BenchTenMyCC(
myCC0: BenchMyCC = BenchMyCC(),
myCC1: BenchMyCC = BenchMyCC(),
myCC2: BenchMyCC = BenchMyCC(),
myCC3: BenchMyCC = BenchMyCC(),
myCC4: BenchMyCC = BenchMyCC(),
myCC5: BenchMyCC = BenchMyCC(),
myCC6: BenchMyCC = BenchMyCC(),
myCC7: BenchMyCC = BenchMyCC(),
myCC8: BenchMyCC = BenchMyCC(),
myCC9: BenchMyCC = BenchMyCC()
)

final case class BenchMyCC(myValue: String = "abc")

object BenchMyCC extends BaseGen {

val fastestMyCCBsonEncoder: BsonEncoder[BenchMyCC] =
BsonEncoder.fastInstance { (writer, value) =>
writer.writeStartDocument()
writer.writeStartDocument("my-cc")
writer.writeString("my-value", value.myValue)
writer.writeEndDocument()
writer.writeEndDocument()
}

val slowMyCCBsonEncoder: BsonEncoder[BenchMyCC] =
BsonEncoder.slowInstance(v => new BsonDocument("my-cc", new BsonDocument("my-value", new BsonString(v.myValue))))

val fastMyCCBsonDecoder: BsonDecoder[BenchMyCC] =
BsonDecoder.fastInstance { reader =>
reader.readStartDocument()
reader.readBsonType()
reader.skipName()
reader.readStartDocument()
reader.readBsonType()
reader.skipName()
val myValue = reader.readString()
reader.readEndDocument()
reader.readEndDocument()
BenchMyCC(myValue)
}

val slowMyCCBsonDecoder: BsonDecoder[BenchMyCC] =
BsonDecoder.slowInstance { bson =>
val myValue = bson.asDocument().getDocument("my-cc").getString("my-value").getValue
BenchMyCC(myValue)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package mongo4cats.bench

import mongo4cats.bench.BenchMyCC.{fastMyCCBsonDecoder, fastestMyCCBsonEncoder, slowMyCCBsonDecoder, slowMyCCBsonEncoder}
import mongo4cats.bench.BsonDecoderInstanceBench.{fastBsonDecoder, slowBsonDecoder, tenMyCCBytes}
import mongo4cats.bench.BsonEncoderInstanceBench.{fastBsonEncoder, tenMyCC}
import mongo4cats.bench.DerivationReadBench.decoderContext
import mongo4cats.bench.DerivationWriteBench.encoderContext
import mongo4cats.derivation.bson.AllBsonEncoders._
import mongo4cats.derivation.bson.AllBsonDecoders._
import mongo4cats.derivation.bson.derivation.decoder.auto._
import mongo4cats.derivation.bson.{BsonDecoder, BsonEncoder}
import org.bson.codecs.EncoderContext
import org.bson.io.BasicOutputBuffer
import org.bson.{BsonBinaryReader, BsonBinaryWriter, BsonDocument, BsonDocumentReader, BsonDocumentWriter, BsonString}
import org.openjdk.jmh.annotations._

import java.nio.ByteBuffer
import java.util.concurrent.TimeUnit

@State(Scope.Thread)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Fork(1)
@Threads(1)
@Warmup(iterations = 2)
@Measurement(iterations = 3)
@Timeout(time = 15)
class BsonDecoderInstanceBench {

@Setup
def setup() = {
val tenMyCCDoc = new BsonDocument()
fastBsonEncoder.unsafeBsonEncode(new BsonDocumentWriter(tenMyCCDoc), tenMyCC, encoderContext)

def decodeFromDoc(bsonDecoder: BsonDecoder[BenchTenMyCC]): BenchTenMyCC =
bsonDecoder.unsafeDecode(new BsonDocumentReader(tenMyCCDoc), decoderContext)

val fastDecoded = decodeFromDoc(fastBsonDecoder)
val slowDecoded = decodeFromDoc(slowBsonDecoder)
// println(fastDoc.toJson)
if (fastDecoded != tenMyCC) throw new Throwable(s"Bad decoder\n$fastDecoded")
if (slowDecoded != tenMyCC) throw new Throwable(s"Bad decoder\n$slowDecoded")
}

@Benchmark
def a_fastBsonDecoder(): Unit = {
tenMyCCBytes.position(0)
val reader = new BsonBinaryReader(tenMyCCBytes)
reader.readBsonType()
// val decoded = //
fastBsonDecoder.unsafeDecode(reader, decoderContext)
// if (decoded != tenMyCC) throw new Throwable(s"${decoded} != $tenMyCC")
()
}

@Benchmark
def b_slowBsonDecoder(): Unit = {
tenMyCCBytes.position(0)
val reader = new BsonBinaryReader(tenMyCCBytes)
reader.readBsonType()
// val decoded = //
slowBsonDecoder.unsafeDecode(reader, decoderContext)
// if (decoded != tenMyCC) throw new Throwable(s"${decoded} != $tenMyCC")
()
}
}

object BsonDecoderInstanceBench {

val fastBsonDecoder: BsonDecoder[BenchTenMyCC] = {
implicit val decoder: BsonDecoder[BenchMyCC] = fastMyCCBsonDecoder
BsonDecoder[BenchTenMyCC]
}

val slowBsonDecoder: BsonDecoder[BenchTenMyCC] = {
implicit val encoder: BsonDecoder[BenchMyCC] = slowMyCCBsonDecoder
BsonDecoder[BenchTenMyCC]
}

val tenMyCCBytes: ByteBuffer = {
val buffer = new BasicOutputBuffer(1000)
fastBsonEncoder.unsafeBsonEncode(new BsonBinaryWriter(buffer), tenMyCC, encoderContext)
ByteBuffer.wrap(buffer.toByteArray)
}
}
Loading