Skip to content

Commit d57d8c5

Browse files
committed
More efficient serialization of IndexedSeq
1 parent 9912fa9 commit d57d8c5

File tree

7 files changed

+155
-4
lines changed

7 files changed

+155
-4
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package com.github.plokhotnyuk.jsoniter_scala.macros
2+
3+
import java.nio.charset.StandardCharsets._
4+
5+
import com.github.plokhotnyuk.jsoniter_scala.core._
6+
import com.github.plokhotnyuk.jsoniter_scala.macros.CirceEncodersDecoders._
7+
import com.github.plokhotnyuk.jsoniter_scala.macros.JacksonSerDesers._
8+
import com.github.plokhotnyuk.jsoniter_scala.macros.JsoniterCodecs._
9+
import io.circe.parser._
10+
import io.circe.syntax._
11+
import org.openjdk.jmh.annotations.Benchmark
12+
import play.api.libs.json.Json
13+
14+
import scala.collection.breakOut
15+
import scala.collection.mutable.ArrayBuffer
16+
17+
class ArrayBufferOfBooleansBenchmark extends CommonParams {
18+
val obj: ArrayBuffer[Boolean] = (1 to 128).map(i => ((i * 1498724053) & 1) == 0)(breakOut)
19+
val jsonString: String = obj.mkString("[", ",", "]")
20+
val jsonBytes: Array[Byte] = jsonString.getBytes
21+
22+
@Benchmark
23+
def readCirce(): ArrayBuffer[Boolean] = decode[ArrayBuffer[Boolean]](new String(jsonBytes, UTF_8)).fold(throw _, x => x)
24+
25+
@Benchmark
26+
def readJacksonScala(): ArrayBuffer[Boolean] = jacksonMapper.readValue[ArrayBuffer[Boolean]](jsonBytes)
27+
28+
@Benchmark
29+
def readJsoniterScala(): ArrayBuffer[Boolean] = readFromArray[ArrayBuffer[Boolean]](jsonBytes)
30+
31+
@Benchmark
32+
def readPlayJson(): ArrayBuffer[Boolean] = Json.parse(jsonBytes).as[ArrayBuffer[Boolean]]
33+
34+
@Benchmark
35+
def writeCirce(): Array[Byte] = printer.pretty(obj.asJson).getBytes(UTF_8)
36+
37+
@Benchmark
38+
def writeJacksonScala(): Array[Byte] = jacksonMapper.writeValueAsBytes(obj)
39+
40+
@Benchmark
41+
def writeJsoniterScala(): Array[Byte] = writeToArray(obj)
42+
43+
@Benchmark
44+
def writeJsoniterScalaPrealloc(): Int = writeToPreallocatedArray(obj, preallocatedBuf, preallocatedOff)
45+
46+
@Benchmark
47+
def writePlayJson(): Array[Byte] = Json.toBytes(Json.toJson(obj))
48+
}

benchmark/src/main/scala/com/github/plokhotnyuk/jsoniter_scala/macros/GoogleMapsAPI.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@ case class Elements(
1414
status: String)
1515

1616
case class DistanceMatrix(
17-
destination_addresses: Seq[String],
18-
origin_addresses: Seq[String],
19-
rows: Seq[Rows],
17+
destination_addresses: IndexedSeq[String],
18+
origin_addresses: IndexedSeq[String],
19+
rows: IndexedSeq[Rows],
2020
status: String)
2121

22-
case class Rows(elements: Seq[Elements])
22+
case class Rows(elements: IndexedSeq[Elements])
2323

2424
object GoogleMapsAPI {
2525
//Distance Matrix API call for top-10 by population cities in US:

benchmark/src/main/scala/com/github/plokhotnyuk/jsoniter_scala/macros/JsoniterCodecs.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import com.github.plokhotnyuk.jsoniter_scala.core.{JsonValueCodec, ReaderConfig}
77
import com.github.plokhotnyuk.jsoniter_scala.macros.JsonCodecMaker.make
88
import com.github.plokhotnyuk.jsoniter_scala.macros.SuitEnum.SuitEnum
99

10+
import scala.collection.mutable
11+
1012
object JsoniterCodecs {
1113
val stacklessExceptionConfig = ReaderConfig(throwParseExceptionWithStackTrace = false)
1214
val stacklessExceptionWithoutDumpConfig =
@@ -19,7 +21,9 @@ object JsoniterCodecs {
1921
implicit val bigIntArrayCodec: JsonValueCodec[Array[BigInt]] = make[Array[BigInt]](CodecMakerConfig())
2022
implicit val bitSetsCodec: JsonValueCodec[BitSets] = make[BitSets](CodecMakerConfig())
2123
implicit val booleanArrayCodec: JsonValueCodec[Array[Boolean]] = make[Array[Boolean]](CodecMakerConfig())
24+
implicit val booleanArrayBufferCodec: JsonValueCodec[mutable.ArrayBuffer[Boolean]] = make[mutable.ArrayBuffer[Boolean]](CodecMakerConfig())
2225
implicit val booleanListCodec: JsonValueCodec[List[Boolean]] = make[List[Boolean]](CodecMakerConfig())
26+
implicit val booleanVectorCodec: JsonValueCodec[Vector[Boolean]] = make[Vector[Boolean]](CodecMakerConfig())
2327
implicit val byteArrayCodec: JsonValueCodec[Array[Byte]] = make[Array[Byte]](CodecMakerConfig())
2428
implicit val charArrayCodec: JsonValueCodec[Array[Char]] = make[Array[Char]](CodecMakerConfig())
2529
implicit val doubleArrayCodec: JsonValueCodec[Array[Double]] = make[Array[Double]](CodecMakerConfig())
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package com.github.plokhotnyuk.jsoniter_scala.macros
2+
3+
import java.nio.charset.StandardCharsets._
4+
5+
import com.github.plokhotnyuk.jsoniter_scala.core._
6+
import com.github.plokhotnyuk.jsoniter_scala.macros.CirceEncodersDecoders._
7+
import com.github.plokhotnyuk.jsoniter_scala.macros.JacksonSerDesers._
8+
import com.github.plokhotnyuk.jsoniter_scala.macros.JsoniterCodecs._
9+
import io.circe.parser._
10+
import io.circe.syntax._
11+
import org.openjdk.jmh.annotations.Benchmark
12+
import play.api.libs.json.Json
13+
14+
import scala.collection.breakOut
15+
16+
class VectorOfBooleansBenchmark extends CommonParams {
17+
val obj: Vector[Boolean] = (1 to 128).map(i => ((i * 1498724053) & 1) == 0)(breakOut)
18+
val jsonString: String = obj.mkString("[", ",", "]")
19+
val jsonBytes: Array[Byte] = jsonString.getBytes
20+
21+
@Benchmark
22+
def readCirce(): Vector[Boolean] = decode[Vector[Boolean]](new String(jsonBytes, UTF_8)).fold(throw _, x => x)
23+
24+
@Benchmark
25+
def readJacksonScala(): Vector[Boolean] = jacksonMapper.readValue[Vector[Boolean]](jsonBytes)
26+
27+
@Benchmark
28+
def readJsoniterScala(): Vector[Boolean] = readFromArray[Vector[Boolean]](jsonBytes)
29+
30+
@Benchmark
31+
def readPlayJson(): Vector[Boolean] = Json.parse(jsonBytes).as[Vector[Boolean]]
32+
33+
@Benchmark
34+
def writeCirce(): Array[Byte] = printer.pretty(obj.asJson).getBytes(UTF_8)
35+
36+
@Benchmark
37+
def writeJacksonScala(): Array[Byte] = jacksonMapper.writeValueAsBytes(obj)
38+
39+
@Benchmark
40+
def writeJsoniterScala(): Array[Byte] = writeToArray(obj)
41+
42+
@Benchmark
43+
def writeJsoniterScalaPrealloc(): Int = writeToPreallocatedArray(obj, preallocatedBuf, preallocatedOff)
44+
45+
@Benchmark
46+
def writePlayJson(): Array[Byte] = Json.toBytes(Json.toJson(obj))
47+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.github.plokhotnyuk.jsoniter_scala.macros
2+
3+
class ArrayBufferOfBooleansBenchmarkSpec extends BenchmarkSpecBase {
4+
val benchmark = new ArrayBufferOfBooleansBenchmark
5+
6+
"ArrayBufferOfBooleansBenchmark" should {
7+
"deserialize properly" in {
8+
benchmark.readCirce() shouldBe benchmark.obj
9+
benchmark.readJacksonScala() shouldBe benchmark.obj
10+
benchmark.readJsoniterScala() shouldBe benchmark.obj
11+
benchmark.readPlayJson() shouldBe benchmark.obj
12+
}
13+
"serialize properly" in {
14+
toString(benchmark.writeCirce()) shouldBe benchmark.jsonString
15+
toString(benchmark.writeJacksonScala()) shouldBe benchmark.jsonString
16+
toString(benchmark.writeJsoniterScala()) shouldBe benchmark.jsonString
17+
toString(benchmark.preallocatedBuf, benchmark.preallocatedOff, benchmark.writeJsoniterScalaPrealloc()) shouldBe benchmark.jsonString
18+
toString(benchmark.writePlayJson()) shouldBe benchmark.jsonString
19+
}
20+
}
21+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.github.plokhotnyuk.jsoniter_scala.macros
2+
3+
class VectorOfBooleansBenchmarkSpec extends BenchmarkSpecBase {
4+
val benchmark = new VectorOfBooleansBenchmark
5+
6+
"VectorOfBooleansBenchmark" should {
7+
"deserialize properly" in {
8+
benchmark.readCirce() shouldBe benchmark.obj
9+
benchmark.readJacksonScala() shouldBe benchmark.obj
10+
benchmark.readJsoniterScala() shouldBe benchmark.obj
11+
benchmark.readPlayJson() shouldBe benchmark.obj
12+
}
13+
"serialize properly" in {
14+
toString(benchmark.writeCirce()) shouldBe benchmark.jsonString
15+
toString(benchmark.writeJacksonScala()) shouldBe benchmark.jsonString
16+
toString(benchmark.writeJsoniterScala()) shouldBe benchmark.jsonString
17+
toString(benchmark.preallocatedBuf, benchmark.preallocatedOff, benchmark.writeJsoniterScalaPrealloc()) shouldBe benchmark.jsonString
18+
toString(benchmark.writePlayJson()) shouldBe benchmark.jsonString
19+
}
20+
}
21+
}

macros/src/main/scala/com/github/plokhotnyuk/jsoniter_scala/macros/JsonCodecMaker.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -781,6 +781,16 @@ object JsonCodecMaker {
781781
l = l.tail
782782
}
783783
out.writeArrayEnd()"""
784+
} else if (tpe <:< typeOf[IndexedSeq[_]]) withEncoderFor(methodKey, m) {
785+
q"""out.writeArrayStart()
786+
val l = x.size
787+
var i = 0
788+
while (i < l) {
789+
out.writeComma()
790+
..${genWriteVal(q"x(i)", typeArg1(tpe), isStringified)}
791+
i += 1
792+
}
793+
out.writeArrayEnd()"""
784794
} else if (tpe <:< typeOf[Traversable[_]]) withEncoderFor(methodKey, m) {
785795
genWriteArray(q"x", genWriteVal(q"x", typeArg1(tpe), isStringified))
786796
} else if (tpe <:< typeOf[Array[_]]) withEncoderFor(methodKey, m) {

0 commit comments

Comments
 (0)