Skip to content

Performance improvements for JacksonConfigParser #218

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

Merged
merged 7 commits into from
Oct 9, 2018

Conversation

loganlinn
Copy link
Contributor

This addresses some poor use of Jackson when deserializing JSON to
ProjectConfig object and improves parse performance by about 15x-40x
based on some benchmarks.

The custom deserializers were creating new ObjectMapper instances on
each deserialize() invocation. Very wasteful.

Eliminates several cases of unecessary round trips of reading JSON
string to JSON tree, then back to JSON string to parse a child node.

We can eliminate the GroupJacksonDeserializer completly and rely on
standard Jackson databind annotations.

Deserialize Audience conditions using JsonNode interface to eliminate
intermediate mapping to List and Map.

Adds JacksonConfigParserBenchmark (fixes Gradle JMH setup).

Benchmark results:

Before:

Benchmark                             Mode  Cnt     Score     Error  Units
JacksonConfigParserBenchmark.parseV2  avgt   40  1668.109 ± 178.586  us/op
JacksonConfigParserBenchmark.parseV3  avgt   40  1574.464 ±  25.816  us/op
JacksonConfigParserBenchmark.parseV4  avgt   40  2166.621 ±  91.664  us/op

After:

Benchmark                             Mode  Cnt    Score   Error  Units
JacksonConfigParserBenchmark.parseV2  avgt   40   41.292 ± 1.219  us/op
JacksonConfigParserBenchmark.parseV3  avgt   40   54.360 ± 0.790  us/op
JacksonConfigParserBenchmark.parseV4  avgt   40  128.589 ± 3.728  us/op

@loganlinn loganlinn requested a review from vraja2 September 24, 2018 19:30
@coveralls
Copy link

coveralls commented Sep 24, 2018

Pull Request Test Coverage Report for Build 684

  • 69 of 74 (93.24%) changed or added relevant lines in 6 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage decreased (-0.2%) to 89.199%

Changes Missing Coverage Covered Lines Changed/Added Lines %
core-api/src/main/java/com/optimizely/ab/config/parser/JacksonHelpers.java 8 9 88.89%
core-api/src/main/java/com/optimizely/ab/config/audience/UserAttribute.java 7 9 77.78%
core-api/src/main/java/com/optimizely/ab/config/parser/AudienceJacksonDeserializer.java 16 18 88.89%
Totals Coverage Status
Change from base Build 682: -0.2%
Covered Lines: 2494
Relevant Lines: 2796

💛 - Coveralls

This addresses some poor use of Jackson when deserializing JSON to
`ProjectConfig` object and improves parse performance by about 15x-40x
based on some benchmarks.

The custom deserializers were creating new `ObjectMapper` instances on
each `deserialize()` invocation. Very wasteful.

Eliminates several cases of unecessary round trips of reading JSON
string to JSON tree, then back to JSON string to parse a child node.

We can eliminate the `GroupJacksonDeserializer` completly and rely on
standard Jackson databind annotations.

Deserialize Audience conditions using `JsonNode` interface to eliminate
intermediate mapping to `List` and `Map`.

Adds `JacksonConfigParserBenchmark` (fixes Gradle JMH setup).

Benchmark results:

Before:

    Benchmark                             Mode  Cnt     Score     Error  Units
    JacksonConfigParserBenchmark.parseV2  avgt   40  1668.109 ± 178.586  us/op
    JacksonConfigParserBenchmark.parseV3  avgt   40  1574.464 ±  25.816  us/op
    JacksonConfigParserBenchmark.parseV4  avgt   40  2166.621 ±  91.664  us/op

After:

    Benchmark                             Mode  Cnt    Score   Error  Units
    JacksonConfigParserBenchmark.parseV2  avgt   40   41.292 ± 1.219  us/op
    JacksonConfigParserBenchmark.parseV3  avgt   40   54.360 ± 0.790  us/op
    JacksonConfigParserBenchmark.parseV4  avgt   40  128.589 ± 3.728  us/op
Copy link
Contributor

@mikeproeng37 mikeproeng37 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for doing this! Especially for adding the benchmark test for showing the results of the improvement =)

Looks good to me. Just sending back so we can remove those two deserializer classes and update the license headers in the files.

@Override
public ProjectConfig deserialize(JsonParser parser, DeserializationContext context) throws IOException {
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(Audience.class, new AudienceJacksonDeserializer());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch! Looks like we won't be needing these two classes anymore. Do you mind removing them as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are still being used -- I moved these into a a proper Module class of their own. See ProjectConfigModule. We can definitely get rid of them with a little more work, but tried to keep the moving parts to a minimum and focus on perf in this PR.

@@ -0,0 +1,9 @@
# EditorConfig is awesome: http://EditorConfig.org
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is pretty cool! Will start using it in our projects :)

@@ -0,0 +1,44 @@
package com.optimizely.ab.config.parser;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add the license header to this file. You only need to include the year 2018 because it was created this year.

import java.util.Collections;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.annotation.*;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please update the license year to 2016-2018 for this and all the files you touched.

Copy link
Contributor

@vraja2 vraja2 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great improvement!

Copy link
Contributor Author

@loganlinn loganlinn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated the copyright headers for files changed in this PR and merged in latest changes from master. @mikeng13 could you take another look?

@Override
public ProjectConfig deserialize(JsonParser parser, DeserializationContext context) throws IOException {
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(Audience.class, new AudienceJacksonDeserializer());
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are still being used -- I moved these into a a proper Module class of their own. See ProjectConfigModule. We can definitely get rid of them with a little more work, but tried to keep the moving parts to a minimum and focus on perf in this PR.

Copy link
Contributor

@mikeproeng37 mikeproeng37 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@mikeproeng37
Copy link
Contributor

build

@mikeproeng37
Copy link
Contributor

@loganlinn looks like there are some lint errors from the Travis build:

Execution failed for task ':core-api:findbugsMain'.
> FindBugs rule violations were found. See the report at: file:///home/travis/build/optimizely/java-sdk/core-api/build/reports/findbugs/main.html```

@loganlinn
Copy link
Contributor Author

@mikeng13 all fixed up

@mikeproeng37
Copy link
Contributor

Thanks! I looked at the Travis builds and they are passing now. HOwever they aren't reporting back to Github. I will merge anyway since all required tests pass. Thanks again for the contribution!

@mikeproeng37 mikeproeng37 merged commit 6366bad into master Oct 9, 2018
@loganlinn loganlinn deleted the llinn/jackson-perf branch October 9, 2018 23:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants