使用 Circe Json 为啥隐式解析在运行时会变慢
Posted
技术标签:
【中文标题】使用 Circe Json 为啥隐式解析在运行时会变慢【英文标题】:With Circe Json why is implicit resolution slower at runtime使用 Circe Json 为什么隐式解析在运行时会变慢 【发布时间】:2017-06-03 11:50:31 【问题描述】:为什么 Circe Json 使用隐式解码器查找比将隐式解码器保存到 val 时更慢。
我希望这些是相同的,因为隐式解析是在运行时完成的。
import io.circe._
import io.circe.generic.auto._
import io.circe.jackson
import io.circe.syntax._
private val decoder = implicitly[Decoder[Data.Type]]
def decode(): Either[Error, Type] =
jackson.decode[Data.Type](Data.json)(decoder)
def decodeAuto(): Either[Error, Type] =
jackson.decode[Data.Type](Data.json)
[info] DecodeTest.circeJackson thrpt 200 69157.472 ± 283.285 ops/s
[info] DecodeTest.circeJacksonAuto thrpt 200 67946.734 ± 315.876 ops/s
完整的 repo 可以在这里找到。 https://github.com/stephennancekivell/some-jmh-json-benchmarks-circe-jackson
【问题讨论】:
Scala 在运行时从不解析隐式 【参考方案1】:考虑这个更简单的情况,根本不涉及循环或泛型推导:
package demo
import org.openjdk.jmh.annotations._
@State(Scope.Thread)
@BenchmarkMode(Array(Mode.Throughput))
class OrderingBench
val items: List[(Char, Int)] = List('z', 'y', 'x').zipWithIndex
val tupleOrdering: Ordering[(Char, Int)] = implicitly
@Benchmark
def sortWithResolved(): List[(Char, Int)] = items.sorted
@Benchmark
def sortWithVal(): List[(Char, Int)] = items.sorted(tupleOrdering)
在我的台式机上的 2.11 上,我得到了这个:
Benchmark Mode Cnt Score Error Units
OrderingBench.sortWithResolved thrpt 40 15940745.279 ± 102634.860 ps/s
OrderingBench.sortWithVal thrpt 40 16420078.932 ± 102901.418 ops/s
如果您查看分配,差异会更大:
Benchmark Mode Cnt Score Error Units
OrderingBench.sortWithResolved:gc.alloc.rate.norm thrpt 20 176.000 ± 0.001 B/op
OrderingBench.sortWithVal:gc.alloc.rate.norm thrpt 20 152.000 ± 0.001 B/op
你可以通过爆破reify
来了解发生了什么:
scala> val items: List[(Char, Int)] = List('z', 'y', 'x').zipWithIndex
items: List[(Char, Int)] = List((z,0), (y,1), (x,2))
scala> import scala.reflect.runtime.universe._
import scala.reflect.runtime.universe._
scala> showCode(reify(items.sorted).tree)
res0: String = $read.items.sorted(Ordering.Tuple2(Ordering.Char, Ordering.Int))
这里的Ordering.Tuple2
是一个实例化Ordering[(Char, Int)]
的通用方法。这与我们定义 tupleOrdering
时发生的事情完全相同,但不同之处在于,在 val
的情况下,它只发生一次,而在隐式解析的情况下,每次调用 sorted
时都会发生。
因此,您看到的区别只是在每个操作中实例化 Decoder
实例的成本,而不是在基准代码之外的开始时单独实例化它。这个成本相对较小,对于更大的基准,它会更难看到。
【讨论】:
非常感谢特拉维斯!以上是关于使用 Circe Json 为啥隐式解析在运行时会变慢的主要内容,如果未能解决你的问题,请参考以下文章
无法使用Circe JSON解析器创建遍历JSON字符串的对象
使用 Argonaut 或 Circe 从不完整的 JSON 更新案例类
使用 Circe 将包含 HList 的案例类解析为 JSON 字符串
使用 circe 时如何在 Scala 中表示动态 JSON 键