-
Notifications
You must be signed in to change notification settings - Fork 190
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
[preview] String parsing improvements #314
Draft
jrudolph
wants to merge
19
commits into
spray:master
Choose a base branch
from
jrudolph:string-parsing-improvements
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from 18 commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
63b9d8d
Update versions of libraries to benchmark against
jrudolph 5564abf
Align crossScalaVersions between sprayJsonJVM and benchmark
jrudolph 03c5044
Add benchmark for parsing (partially) to case class instances
jrudolph 06e641c
Add just-strings benchmark
jrudolph 515ffc9
update jmh and play-json for benchmarking
jrudolph 1a074b9
Add jackson as comparison for case class reading
jrudolph c7b12f0
implement fast path for string parsing
jrudolph 6c001f4
improve string parsing
jrudolph 5fd15a6
wip
jrudolph 19f1cb7
Ignore error message parser tests for now
jrudolph e22eac8
Faster string parsing
jrudolph 24f63cb
Fast slow parsing
jrudolph c94ed53
Some refactoring for clarity
jrudolph 22b9e8b
Introduce new setting for maximum string size
jrudolph 27ea4f3
Automatically scale char buffer up to a given size
jrudolph d4ef762
disable parseOneFast for now as it doesn't seem to help
jrudolph 8a867c8
Remove now (almost) unused nextUtf8Char
jrudolph 2288c5e
Cache buffer in thread local between runs
jrudolph da793e6
Enable `parseOneFast` again after actually making it fast by using `c…
jrudolph File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
168 changes: 168 additions & 0 deletions
168
benchmark/src/main/scala/spray/json/GithubIssuesCaseClassReading.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
package spray.json | ||
|
||
import com.fasterxml.jackson.databind.DeserializationFeature | ||
import org.openjdk.jmh.annotations.Benchmark | ||
import org.openjdk.jmh.annotations.Setup | ||
import com.fasterxml.jackson.databind.ObjectMapper | ||
import com.fasterxml.jackson.databind.module.SimpleModule | ||
import com.fasterxml.jackson.module.scala.experimental.ScalaObjectMapper | ||
import com.fasterxml.jackson.module.scala.DefaultScalaModule | ||
|
||
import scala.io.Source | ||
|
||
object GithubIssuesPartialModel { | ||
import DefaultJsonProtocol._ | ||
case class LabelsArrayElement( | ||
id: Long, | ||
url: String, | ||
//node_id: String, | ||
name: String, | ||
color: String, | ||
default: Boolean) | ||
|
||
implicit val LabelsArrayElementFormat = jsonFormat5(LabelsArrayElement.apply _) | ||
|
||
case class User( | ||
login: String, | ||
id: Long, | ||
node_id: String /*, | ||
//gists_url: String, | ||
//organizations_url: String, | ||
gravatar_id: String, | ||
//url: String, | ||
|
||
//repos_url: String, | ||
//received_events_url: String, | ||
|
||
//following_url: String, | ||
site_admin: Boolean, | ||
//subscriptions_url: String, | ||
//starred_url: String, | ||
//html_url: String, | ||
|
||
`type`: String, | ||
//events_url: String, | ||
//avatar_url: String, | ||
followers_url: String*/ ) | ||
|
||
implicit val UserFormat = jsonFormat3(User.apply _) | ||
|
||
case class Pull_requestOptionElement( | ||
diff_url: String, | ||
html_url: String, | ||
patch_url: String, | ||
url: String) | ||
|
||
implicit val Pull_requestOptionElementFormat = jsonFormat4(Pull_requestOptionElement.apply _) | ||
|
||
case class RootArrayElement( | ||
url: String, | ||
repository_url: String, | ||
//labels_url: String, | ||
comments_url: String, | ||
//events_url: String, | ||
html_url: String, | ||
id: Long, | ||
//node_id: String, | ||
number: Long, | ||
title: String, | ||
user: User, | ||
//labels: Seq[LabelsArrayElement], | ||
state: String, | ||
|
||
locked: Boolean, | ||
//assignee: Option[User], | ||
assignees: Seq[User], | ||
created_at: String, | ||
|
||
//body: Option[String], | ||
//milestone: Option[String], | ||
//closed_at: Option[String], | ||
|
||
updated_at: String, | ||
author_association: String | ||
|
||
//, | ||
//comments: BigDecimal//, | ||
//pull_request: Option[Pull_requestOptionElement] | ||
) | ||
|
||
implicit val RootArrayElementFormat = jsonFormat14(RootArrayElement.apply _) | ||
} | ||
|
||
class GithubIssuesCaseClassReading extends Common { | ||
var jsonString: String = _ | ||
var jsonBytes: Array[Byte] = _ | ||
|
||
implicit val playJsonRootFormat = { | ||
import GithubIssuesPartialModel._ | ||
import play.api.libs.json._ | ||
implicit val playJsonUserFormat: Format[User] = play.api.libs.json.Json.format | ||
implicit val playJsonLabelFormat: Format[LabelsArrayElement] = play.api.libs.json.Json.format | ||
play.api.libs.json.Json.format: Format[RootArrayElement] | ||
} | ||
|
||
implicit def circeRootFormat = { | ||
import GithubIssuesPartialModel._ | ||
import io.circe._ | ||
import io.circe.generic.semiauto._ | ||
import io.circe.parser._ | ||
implicit def circeUserFormat: Decoder[User] = deriveDecoder | ||
implicit def circeLabelFormat: Decoder[LabelsArrayElement] = deriveDecoder | ||
|
||
deriveDecoder: Decoder[RootArrayElement] | ||
} | ||
|
||
implicit def upickleDefaultFormat = { | ||
import GithubIssuesPartialModel._ | ||
import upickle.default.{ ReadWriter => RW, Reader => R, Writer => W } | ||
implicit val userFormat: R[User] = upickle.default.macroR[User] | ||
implicit val labelFormat: R[LabelsArrayElement] = upickle.default.macroR[LabelsArrayElement] | ||
upickle.default.macroR[RootArrayElement] | ||
} | ||
|
||
val jacksonMapper: ObjectMapper with ScalaObjectMapper = new ObjectMapper with ScalaObjectMapper { | ||
registerModule(DefaultScalaModule) | ||
configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) | ||
} | ||
|
||
@Setup | ||
def setup(): Unit = { | ||
jsonString = Source.fromResource("github-akka-issues.json").mkString | ||
jsonBytes = jsonString.getBytes("utf8") // yeah, a useless utf8 roundtrip | ||
} | ||
|
||
@Benchmark | ||
def readSprayJsonViaASTPartial(): AnyRef = { | ||
import DefaultJsonProtocol._ | ||
JsonParser(jsonBytes).convertTo[Seq[GithubIssuesPartialModel.RootArrayElement]] | ||
} | ||
|
||
@Benchmark | ||
def readPlayJsonPartial(): AnyRef = { | ||
import play.api.libs.json.Json | ||
Json.fromJson[Seq[GithubIssuesPartialModel.RootArrayElement]](Json.parse(jsonBytes)).get | ||
} | ||
|
||
@Benchmark | ||
def readJacksonPartial(): AnyRef = { | ||
jacksonMapper.readValue[Array[GithubIssuesPartialModel.RootArrayElement]](jsonBytes) | ||
//Json.fromJson[Seq[GithubIssuesPartialModel.RootArrayElement]](Json.parse(jsonBytes)).get | ||
} | ||
|
||
/* | ||
Fails with "upickle.core.Abort: expected sequence got int32" | ||
@Benchmark | ||
def readUPickleDefaultBinaryPartial(): Unit = | ||
upickle.default.readBinary[Seq[GithubIssuesPartialModel.RootArrayElement]](jsonBytes) | ||
|
||
*/ | ||
|
||
@Benchmark | ||
def readCircePartial(): AnyRef = { | ||
import io.circe._ | ||
import io.circe.generic.semiauto._ | ||
import io.circe.parser._ | ||
decode[Seq[GithubIssuesPartialModel.RootArrayElement]](jsonString).right.get | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
@jrudolph please consider to cache derived decoders using
val
for better performance