7

Multiple posts on the Internet point toward Jackson as having better parsing performance than GSON, granting somewhere in the neighborhood of 20-30% speed improvement.

Pulling out our GSON parser and replacing with Jackson resulted in a 7x slowdown in my project, with latency of over 300 ms per invocation. The same parse job on GSON takes less than 50 ms.

I went through the list of "gotchas" on the Jackson Wiki, but nothing there stood out as a red flag.

For example, I'm not recreating my ObjectMapper, and I'm using ObjectReader to read all the JSON. Here is some sample code:

public class JsonParser {
    @Nonnull
    private final ObjectMapper objectMapper;

    public JsonParser() {
        final ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setDateFormat(DateFormatUtil.getGmtIso8601DateFormat());

        SimpleModule simpleModule = new SimpleModule();
        objectMapper.registerModule(simpleModule);
        this.objectMapper = objectMapper;
    }

    public <T> T fromJson(InputStream inputStream, Class<T> clazz) throws IOException {
        ObjectReader reader = objectMapper.reader(clazz);
        return reader.readValue(inputStream);
    }
}

The object above gets created once and is used for the duration of the app to translate JSON into POJOs. A sample POJO can be seen here:

@JsonSerialize(include= Inclusion.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class ActivityEntity {
    public ActivityObjectEntity actor;
    public ActivityObjectEntity object;
    public ActivityObjectEntity provider;
    public ActivityObjectEntity target;
    public ActivityObjectEntity generator;
    public String content;
    public String title;
    public String verb;
    public String url;
    public Date published;
    public Date updated;
    // other properties omitted ...
}

What is being serialized is actually a list of the above items.

Here are my sample trace view windows from each run. Note, this is not an anomaly. I consistently get the same order of performance out of both Gson and Jackson parsing the same dataset.

Comparison was with Jackson 2.4.2 and Gson 2.2.4

traceview output from Jackson traceview output from Gson

markshiz
  • 2,501
  • 22
  • 28

1 Answers1

3

Code looks correct, and even at its worst, Jackson should be no slower than Gson; and certainly not by multiples of anything.

If you were able to get a profiler snapshot for call stack (for running deserialization continuously for 10+ seconds), that would probably point out where excess time is spent, and could help figure out the culprit.

I would still double-check that JsonParser is not inadvertently constructed multiple times: one tricky case is for example via frameworks like Jersey (directly or via DropWizard) that may construct resources multiple times unless told to construct and use singleton instances. I say this because symptoms just fit this case well, not because I doubt that you hadn't done due diligence.

StaxMan
  • 113,358
  • 34
  • 211
  • 239
  • I have a log in my code for each time the object is created and it only happens once at startup, so it is definitely not being created more than once. – markshiz Oct 08 '14 at 21:24
  • The existing stack trace that is attached to the question is probably sufficient for investigation purposes. I'm not familiar with Jackson, so it means very little to me. Almost 100% of the time is spent in DeserializerCache._createAndCache2 with the hot trace from there being BeanDesierliazerBase.resolve at 60%. – markshiz Oct 08 '14 at 21:27
  • FWIW, that hot-trace is all inside the ObjectReader construction. – markshiz Oct 08 '14 at 21:34
  • @markshiz it is unfortunately bit hard to read (although zooming does show some of it). Another possibility is if you could share a gist of object defs. However -- `resolve` (etc) really should NOT be there -- it's a one-time thing, and resulting deserializer is cached. Which is why I'd still suspect lack of reuse for `ObjectMapper` (or something similar) – StaxMan Oct 08 '14 at 22:23
  • 1
    Looks like I was getting a misleading trace from Android's traceview. The profiler was highlighting what I can only presume is the initialization trace. When I run the code in a loop in a JUnit test and compare times, Jackson is roughly 10% faster. – markshiz Oct 08 '14 at 23:13
  • @markshiz Ok that sounds more typical result. First call will have most of the overhead (construction of (de)serializers is bit involved in general, but Android's Reflection/Annotation access is notoriously slow, unfortuantely), but speed should converge quickly after that. – StaxMan Oct 08 '14 at 23:48
  • 4
    Note that performance on Android doesn't correlate with performance on desktop Java. Gson is optimized for Android, so that it's fast the first time you parse a document. Other parsers are tuned for throughput when you're parsing a lot of documents. – Jesse Wilson Oct 09 '14 at 04:16
  • 1
    FWIW, discounting the initial parser construction costs, further traces on the Android runtime reveal Jackson to be much faster than Gson. – markshiz Oct 09 '14 at 15:45
  • 1
    I agree with @JesseWilson, there are differing strategies here. One last note is that (yet) another possibility is `Jackson Jr` (https://github.com/FasterXML/jackson-jr) which also has lower first-use overhead, and is geared more for Android use cases. – StaxMan Oct 09 '14 at 17:32