2017-01-18 14 views
9

Почему Circe Json медленнее с неявным декодером по сравнению с сохранением неявного декодера в val.С Circe Json почему неявное разрешение медленнее во время выполнения

Я ожидаю, что они будут такими же, поскольку неявное разрешение выполняется во время выполнения.

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 

Полное репо можно найти здесь. https://github.com/stephennancekivell/some-jmh-json-benchmarks-circe-jackson

+1

Scala никогда не разрешает неявное во время выполнения – cchantep

ответ

17

Рассмотрим более простой случай, не связанный Цирцея или общий вывод на все:

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)) 

The Ordering.Tuple2 здесь является общий метод, который создает экземпляр Ordering[(Char, Int)]. Это то же самое, что и при определении нашего tupleOrdering, но разница в том, что в случае val это происходит один раз, тогда как в случае, когда он разрешен неявно, это происходит каждый раз, когда вызывается sorted.

Таким образом, разница, которую вы видите, - это всего лишь стоимость создания экземпляра Decoder в каждой операции, в отличие от его экземпляра за один раз в начале за пределами контрольного кода. Эта стоимость относительно крошечная, и для более крупных тестов это будет труднее увидеть.

+4

Большое спасибо Travis! – Stephen