Skip to content

Jakarta JSON-P Json.*** methods are crazily inefficient #42748

@gsmet

Description

@gsmet

The low-level static method hosted in the Jakarta JSON-P Json object are very inefficient: they are resolving a JsonProvider.provider() for each call which in turn calls the ServiceLoader (among other things...) and allocate a new JsonProvider.

See:

Meaning that when you are calling Json.createValue("string") to allocate a simple String value, you are actually making a call to the service loader and instantiating a new JsonProvider.

I stumbled upon that independently this week-end and after experimenting with it and writing a quick patch, I was like "it's crazy nobody complained about it" so I went to the JSON-P website and stumbled upon:

Meaning that I don't expect it to be fixed any time soon.

To give an idea of how things go, I did a simple JMH benchmark.

Json.createValue("test") i.e. what people (including us) use:

Iteration   1: 153.907 ops/ms
Iteration   2: 155.308 ops/ms
Iteration   3: 154.542 ops/ms
Iteration   4: 155.963 ops/ms
Iteration   5: 156.462 ops/ms

Using a static provider with JSON_PROVIDER.createValue("test"):

Iteration   1: 1112305.967 ops/ms
Iteration   2: 1114183.052 ops/ms
Iteration   3: 1113783.793 ops/ms
Iteration   4: 1111395.048 ops/ms
Iteration   5: 1112449.325 ops/ms

That's 7200+ times faster.
The createValue() methods are the ones for which it's the most inefficient as they should be extremely lightweight as called quite often.

While we are mostly using Jackson in Quarkus itself and recommending Jackson, we have several components using JSON-P and JSON-B in the SmallRye world, for instance SmallRye GraphQL and SmallRye Health.
And we have people using JSON-P/JSON-B out there because they want to stay in the MicroProfile or Jakarta world.

I know SmallRye GraphQL is affected by the inefficiencies as I have seen some direct calls to the Json methods in some runtime code (for instance here: https://github.com/smallrye/smallrye-graphql/blob/main/client/implementation/src/main/java/io/smallrye/graphql/client/impl/RequestImpl.java#L75).
From what I can see SmallRye Health is not affected as it creates a unique JsonProvider.

Note that I haven't done any benchmarks in SmallRye GraphQL, I have just verified that we were instantiating too many JsonProviders.

As for the SmallRye components, I think we should discuss this subject in the context of Quarkus as the solution we want to put in place for Quarkus might be different from the ones we want to use in the general case (for instance in WildFly).

  • we might want to have an SPI to push Jackson there. I discussed it a few times with @jmartisk already but it wasn't considered top priority
  • even if we do, we probably need to fix the JSON-P/JSON-B case - while fixing our JSON-P calls could be easy (at least in the Quarkus case, I'm not sure if in the case of WildFly, we would want a single provider), I don't know if JSON-B uses the Json shortcut under the hood, in which case it might make things a bit harder to handle.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions