Skip to content

Commit

Permalink
checkpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
Greg Zoller committed Nov 16, 2023
1 parent caaa5e3 commit 4a7b716
Show file tree
Hide file tree
Showing 22 changed files with 3,380 additions and 263 deletions.
7 changes: 4 additions & 3 deletions benchmark/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ sbt "jmh:run -i 10 -wi 10 -f 2 -t 1 co.blocke.*"
| Benchmark | Mode | Count | Score | Error | Units |
|------------------|-------|-------:|----------------:|-------------:|-------|
| Jsoniter | thrpt | 20 | 987991.329 | ± 6645.992 | ops/s |
| **ScalaJack 8** | thrpt | 20 | **633764.943**| ± 10394.860 | ops/s |
| **ScalaJack 8 (fast mode)** | thrpt | 20 | **642235.553**| ± 10394.860 | ops/s |
| ZIOJson | thrpt | 20 | 586716.228 | ± 2542.783 | ops/s |
| **ScalaJack 8 (easy mode)** | thrpt | 20 | **426718.318**| ± 692.828 | ops/s |
| Circe | thrpt | 20 | 266568.198 | ± 5695.754 | ops/s |
| Play | thrpt | 20 | 207737.560 | ± 842.108 | ops/s |
| Argonaut | thrpt | 20 | 197876.777 | ± 11181.751 | ops/s |
Expand Down Expand Up @@ -59,8 +60,8 @@ I observed the older serializers processed JSON to/from an AST and used conventi
parsing techniques; basically fortified editions of a simple JSON parser. ZIO-Json's
impressive read performance wasn't achieved by any one thing, but rather a collection of well-
applied techniques, including *not* using an intermediate AST. So naturally I incorporated
some of ZIO-Json's approach (and a bit of their code), stripped, refitted, and adapted to
ScalaJack, and read performance jumped to 633K. Nice!
some of ZIO-Json's approach (and a bit of their code) for JSON reading, stripped, refitted,
and adapted to ScalaJack, and read performance jumped to 633K. Nice!

Jsoniter, it turns out, achieves its neck-breaking speed by going deep--very deep. They
use a lot of low level byte arrays and bitwise operators, much as you'd expect in a C program,
Expand Down
3 changes: 1 addition & 2 deletions benchmark/build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,7 @@ lazy val benchmark = project
libraryDependencies ++= Seq(
"org.playframework" %% "play-json" % "3.0.1",
"io.argonaut" %% "argonaut" % "6.3.9",
// "co.blocke" %% "scalajack" % "826a30_unknown", // Old-New
"co.blocke" %% "scalajack" % "e48b35_unknown", // New-New
"co.blocke" %% "scalajack" % "caaa5e_unknown", // New-New
"co.blocke" %% "scala-reflection" % "sj_fixes_edbef8",
"dev.zio" %% "zio-json" % "0.6.1",
"org.typelevel" %% "fabric-core" % "1.12.6",
Expand Down
26 changes: 13 additions & 13 deletions benchmark/src/main/scala/co.blocke/Benchmark.scala
Original file line number Diff line number Diff line change
Expand Up @@ -43,24 +43,24 @@ trait HandTooledWritingBenchmark {
@BenchmarkMode(Array(Mode.Throughput))
@OutputTimeUnit(TimeUnit.SECONDS)
class ReadingBenchmark
extends CirceZ.CirceReadingBenchmark
with ScalaJackZ.ScalaJackReadingBenchmark
with JsoniterZ.JsoniterReadingBenchmark
with ZIOZ.ZIOJsonReadingBenchmark
with PlayZ.PlayReadingBenchmark
with ArgonautZ.ArgonautReadingBenchmark
// extends CirceZ.CirceReadingBenchmark
extends ScalaJackZ.ScalaJackReadingBenchmark
// with JsoniterZ.JsoniterReadingBenchmark
// with ZIOZ.ZIOJsonReadingBenchmark
// with PlayZ.PlayReadingBenchmark
// with ArgonautZ.ArgonautReadingBenchmark

@State(Scope.Thread)
@BenchmarkMode(Array(Mode.Throughput))
@OutputTimeUnit(TimeUnit.SECONDS)
class WritingBenchmark
extends HandTooledWritingBenchmark
with CirceZ.CirceWritingBenchmark
with ScalaJackZ.ScalaJackWritingBenchmark
with JsoniterZ.JsoniterWritingBenchmark
with ZIOZ.ZIOJsonWritingBenchmark
with PlayZ.PlayWritingBenchmark
with ArgonautZ.ArgonautWritingBenchmark
// extends HandTooledWritingBenchmark
// with CirceZ.CirceWritingBenchmark
extends ScalaJackZ.ScalaJackWritingBenchmark
// with JsoniterZ.JsoniterWritingBenchmark
// with ZIOZ.ZIOJsonWritingBenchmark
// with PlayZ.PlayWritingBenchmark
// with ArgonautZ.ArgonautWritingBenchmark

// "Old-New" ScalaJack
// [info] Benchmark Mode Cnt Score Error Units
Expand Down
34 changes: 6 additions & 28 deletions benchmark/src/main/scala/co.blocke/ScalaJack.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,17 @@ import org.openjdk.jmh.annotations._
object ScalaJackZ:
import co.blocke.scalajack.*

import json2.*

implicit val addrDecoder: JsonDecoder[Address] = ClassDecoder(
Array("street", "city", "state", "postal_code"),
Array(JsonDecoder[String], JsonDecoder[String], JsonDecoder[String], JsonDecoder[String])
)
implicit val friendDecoder: JsonDecoder[Friend] = ClassDecoder(
Array("name", "age", "email"),
Array(JsonDecoder[String], JsonDecoder[Int], JsonDecoder[String])
)
implicit val petDecoder: JsonDecoder[Pet] = ClassDecoder(
Array("name", "species", "age"),
Array(JsonDecoder[String], JsonDecoder[String], JsonDecoder[Int])
)
implicit val PersonDecoder: JsonDecoder[Person] = ClassDecoder(
Array("name", "age", "address", "email", "phone_numbers", "is_employed"),
Array(JsonDecoder[String], JsonDecoder[Int], JsonDecoder[Address], JsonDecoder[String], JsonDecoder[List[String]], JsonDecoder[Boolean])
)
implicit val RecordDecoder: JsonDecoder[Record] = ClassDecoder(
Array("person", "hobbies", "friends", "pets"),
Array(JsonDecoder[Person], JsonDecoder[List[String]], JsonDecoder[List[Friend]], JsonDecoder[List[Pet]])
)

// val jp = co.blocke.scalajack.json.JsonParser3(jsData)
import json.*

implicit val blah: sj[Record] = ScalaJack.inspect[Record]

trait ScalaJackReadingBenchmark{
@Benchmark
// def readRecordScalaJack = { jp.reset(); jp.parse() }
// def readRecordScalaJack = ScalaJack.read[Record](jsData)
def readRecordScalaJack = JsonDecoder[Record].decodeJson(jsData)
}
def readRecordScalaJack = sj[Record].decodeJson(jsData)
}

trait ScalaJackWritingBenchmark {
@Benchmark
def writeRecordScalaJack = ScalaJack.write(record)
}
}
53 changes: 53 additions & 0 deletions rnd/build.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
inThisBuild(List(
organization := "co.blocke",
homepage := Some(url("https://github.com/gzoller/ScalaJack")),
licenses := List("MIT" -> url("https://opensource.org/licenses/MIT")),
developers := List(
Developer(
"gzoller",
"Greg Zoller",
"[email protected]",
url("http://www.blocke.co")
)
)
))

name := "rnd"
ThisBuild / organization := "co.blocke"
ThisBuild / scalaVersion := "3.3.0"

lazy val root = project
.in(file("."))
.settings(settings)
.settings(
name := "serializer",
Compile / packageBin / mappings += {
(baseDirectory.value / "plugin.properties") -> "plugin.properties"
},
doc := null, // disable dottydoc for now
Compile / doc / sources := Seq(),
//sources in (Compile, doc) := Seq(),
Test / parallelExecution := false,
libraryDependencies ++= Seq(
"org.scalatest" %% "scalatest" % "3.2.17" % Test
)
)

//==========================
// Settings
//==========================
lazy val settings = Seq(
javacOptions ++= Seq("-source", "1.8", "-target", "1.8"),
scalacOptions ++= compilerOptions
)

lazy val compilerOptions = Seq(
"-unchecked",
"-feature",
"-language:implicitConversions",
"-deprecation",
// "-explain",
"-encoding",
"utf8"
)

1 change: 1 addition & 0 deletions rnd/project/build.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
sbt.version=1.9.6
6 changes: 6 additions & 0 deletions rnd/project/metals.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// DO NOT EDIT! This file is auto-generated.

// This file enables sbt-bloop to create bloop config files.

addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.5.11")

1 change: 1 addition & 0 deletions rnd/project/plugins.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.2")
46 changes: 46 additions & 0 deletions rnd/src/main/java/co/blocke/scalajack/ByteArrayAccess.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package co.blocke.scalajack;

import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.nio.ByteOrder;

class ByteArrayAccess { // FIXME: Use Java wrapper as w/a for missing support of @PolymorphicSignature methods in Scala 3, see: https://github.com/lampepfl/dotty/issues/11332
private static final VarHandle VH_LONG =
MethodHandles.byteArrayViewVarHandle(long[].class, ByteOrder.LITTLE_ENDIAN);
private static final VarHandle VH_INT =
MethodHandles.byteArrayViewVarHandle(int[].class, ByteOrder.LITTLE_ENDIAN);
private static final VarHandle VH_SHORT =
MethodHandles.byteArrayViewVarHandle(short[].class, ByteOrder.LITTLE_ENDIAN);
private static final VarHandle VH_LONG_REVERSED =
MethodHandles.byteArrayViewVarHandle(long[].class, ByteOrder.BIG_ENDIAN);
private static final VarHandle VH_INT_REVERSED =
MethodHandles.byteArrayViewVarHandle(int[].class, ByteOrder.BIG_ENDIAN);

static void setLong(byte[] buf, int pos, long value) {
VH_LONG.set(buf, pos, value);
}

static long getLong(byte[] buf, int pos) {
return (long) VH_LONG.get(buf, pos);
}

static void setInt(byte[] buf, int pos, int value) {
VH_INT.set(buf, pos, value);
}

static int getInt(byte[] buf, int pos) {
return (int) VH_INT.get(buf, pos);
}

static void setShort(byte[] buf, int pos, short value) {
VH_SHORT.set(buf, pos, value);
}

static void setLongReversed(byte[] buf, int pos, long value) {
VH_LONG_REVERSED.set(buf, pos, value);
}

static int getIntReversed(byte[] buf, int pos) {
return (int) VH_INT_REVERSED.get(buf, pos);
}
}
Loading

0 comments on commit 4a7b716

Please sign in to comment.