考虑这个更简单的情况,它根本不涉及 circe 或泛型推导:
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
你可以通过 break out 来判断发生了什么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)]
. 这与我们定义 our 时发生的事情完全相同tupleOrdering
,但不同之处在于,在val
它发生一次的情况下,在它被隐式解析的情况下,它每次都sorted
被调用。
因此,您看到的区别只是Decoder
在每个操作中实例化实例的成本,而不是在基准代码之外的开始时一次实例化它。这个成本相对较小,对于更大的基准,它会更难看到。