Cached/lazy/interned macro materializers or implicits?

191 views
Skip to first unread message

Haoyi Li

unread,
Aug 13, 2014, 11:44:43 PM8/13/14
to scala-l...@googlegroups.com
I'm working on a macro-based pickling library upickle, and one thing I do is that I do this whole implicit-resolution-to-find-picklers thing, e.g.

write[Int](...) -> write[Int](...)(intWriter)
write[Seq[Int]](...) -> write[Int](...)(SeqWriter(intWriter))
write[MyCaseClass](...) -> write[Int](...)(Writer.macroWriter) -> write[Int](...)(...expanded code...)
Well, one thing I realized recently that in my benchmarks, the *act of instantiating the implicit* is sufficient to make a real impact on the performance of the pickling! For example, my benchmark basically tests:

sealed trait A
case class B(i: Int) extends A
case class C(s1: String, s2: String) extends A
sealed trait LL
case object End  extends LL
case class Node(c: Int, next: LL) extends LL
case class ADT0()
case class ADTc(i: Int = 2, s: String, t: (Double, Double) = (1, 2))
type Data = ADT[Seq[(Int, Int)], String, A, LL, ADTc, ADT0]

read[Data](...: String)
write[Data](...: Data)
When I pre-instantiate the readers and writers outside my while-loop, my perf numbers (higher is better) are

[info]     jvm/read Success(306933)
[info]     jvm/write Success(330214)
[info]     js/read Success(34296)
[info]     js/write Success(25559)

On the other hand, if I leave the instantiation to the implicit resolution, and thus inside the while loop, the numbers are 

[info]     jvm/read Success(260349)
[info]     jvm/write Success(313252)
[info]     js/read Success(23268)
[info]     js/write Success(18099)

This is consistent over many runs, and all that stuff. It turns out that in Scala.js around 1/3 of my time is simply spent instantiating my (non-trivial) materializers! Even on the JVM, there is a noticeable 5-10% perf hit from doing this every time.

Now, every single one of these implicits and the macro-materializers are "pure"; they serve no other purpose other than to make the whole typeclass-pattern-thing work, and the exact same structure is going to be instantiated every single time this line of code is executed.

This leads up to my point: is there a way to mark an implicit as "cached", such that (similar to a lazy val) it gets stored somewhere after being calculated the first time (with a mutex or whatever), and there after it always returns the same instance of the thing rather than re-instantiating it each time? 

I imagine that for people using generic typeclasses (for non-generic ones you can just make the implicit a val), a large fraction of their implicits materialize "pure" objects with no internal state and which can be safely shared across any and all invocations of the callsite. That would save a significant amount of garbage being generated make a bunch of things run faster, similar to lifting non-closure-lambdas into static-lambdas. 

Thoughts?

Eugene Burmako

unread,
Aug 14, 2014, 5:00:17 AM8/14/14
to scala-l...@googlegroups.com


--
You received this message because you are subscribed to the Google Groups "scala-language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-languag...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Miles Sabin

unread,
Aug 16, 2014, 5:53:40 AM8/16/14
to scala-l...@googlegroups.com
On Thu, Aug 14, 2014 at 4:44 AM, Haoyi Li <haoy...@gmail.com> wrote:
> This leads up to my point: is there a way to mark an implicit as "cached",
> such that (similar to a lazy val) it gets stored somewhere after being
> calculated the first time (with a mutex or whatever), and there after it
> always returns the same instance of the thing rather than re-instantiating
> it each time?

You could take a look at shapeless's Lazy[T],

https://github.com/milessabin/shapeless/blob/master/core/src/main/scala/shapeless/lazy.scala

This was designed specifically to support lazy recursive implicit knot
tying, which I imagine would also be of interest to you ... the trick
is the nested implicit self-publication.

Cheers,


Miles

--
Miles Sabin
tel: +44 7813 944 528
skype: milessabin
gtalk: mi...@milessabin.com
g+: http://www.milessabin.com
http://twitter.com/milessabin

Haoyi Li

unread,
Aug 16, 2014, 10:50:29 AM8/16/14
to scala-l...@googlegroups.com
I already got a lazy implicit knot thing for uPickle =) It's lazy, but still evaluated/constructed once every time the code comes around...


Reply all
Reply to author
Forward
0 new messages