-
Notifications
You must be signed in to change notification settings - Fork 73
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Optimize UnwrapArray parser by caching JValue ClassTag #547
Conversation
@@ -81,6 +82,7 @@ sealed abstract class JValue { | |||
|
|||
object JValue { | |||
implicit val facade: Facade[JValue] = JawnFacade | |||
implicit val classTag: ClassTag[JValue] = ClassTag(classOf[JValue]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I wonder if it's worth making this lazy
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What are you trying to optimize? The memory consumption?
On the other hand the UnwrapArray
parser would have to repeatedly go through the synchronized access.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Load time. Companion object gets initialized the moment the type gets used so I thought it might be worth it to defer reflection call until the code path hits it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds reasonable 👍
I made it lazy.
@@ -81,6 +82,7 @@ sealed abstract class JValue { | |||
|
|||
object JValue { | |||
implicit val facade: Facade[JValue] = JawnFacade | |||
implicit lazy val classTag: ClassTag[JValue] = ClassTag(classOf[JValue]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @agourlay !
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like it, but one question since I'm a public API minimalist...
@@ -81,6 +82,7 @@ sealed abstract class JValue { | |||
|
|||
object JValue { | |||
implicit val facade: Facade[JValue] = JawnFacade | |||
implicit lazy val classTag: ClassTag[JValue] = ClassTag(classOf[JValue]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Complete nitpick, but does this need to be public? Seems like an implementation detail.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Totally legit nitpick, I made it private in c61b951
I had to add a scope to the private field otherwise the compilation fails with
[error] /home/agourlay/Workspace/jawn/ast/shared/src/main/scala/jawn/ast/JawnFacade.scala:56:55: lazy value classTag in object JValue cannot be accessed in object org.typelevel.jawn.ast.JValue
[error] Access to protected lazy value classTag not permitted because
[error] enclosing <$anon: org.typelevel.jawn.FContext.NoIndexFContext[org.typelevel.jawn.ast.JValue]> is not a subclass of
[error] object JValue in package ast where target is defined
[error] def finish(): JValue = JArray(vs.toArray(JValue.classTag))
@@ -81,6 +82,7 @@ sealed abstract class JValue { | |||
|
|||
object JValue { | |||
implicit val facade: Facade[JValue] = JawnFacade | |||
implicit private[jawn] lazy val classTag: ClassTag[JValue] = ClassTag(classOf[JValue]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note that lazy
adds some additional synchronization overhead compared to a vanilla val
, which is why we have unsafe lazies in Scala 3.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose it could be rewritten as a null init and probably squeeze out a few more femtoseconds.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Re: val
vs lazy val
, I didn't confirm with a benchmark, but my speculation was that val
would regress the load time for users who just see JValue
as type signature but do not hit the UnwrapArray parser.
#547 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, sure :) basically we are trading off a one-time load penalty for a penalty on every access.
Thank you very much for the super fast release 👏 I was able to validate that I get a nice performance boost from this change. |
While using the
UnwrapArray
parsing mode, I can see a very large amount ofjava.lang.ClassValue
calls.This accounts for almost 30% of CPU time for very large inputs.
This PR proposes to cache an implicit
ClassTag
value for theJValue
in order to speed things up.I was unfortunately not able to validate the fix as I have issues producing snapshots locally.