FasterXML / FasterXML/jackson-core
No-allocation DoubleToDecimal / FloatToDecimal
- Dominant language
- Java
- Stars
- 2.4k
- Forks
- 928
- Avg merge
- 2d 18h
- Merged PRs (30d)
- 24
Description
OpenJDK25 added implementations of `DoubleToDecimal` and `FloatToDecimal` that encode directly to Latin1 bytes without any allocations: https://github.com/openjdk/jdk/commit/9d20b58f40275002afa0348d94d5592a26894e88
If this was public API, it could be used pretty easily within UTF8JsonGenerator for some fairly massive performance benefits when serializing large numbers of floating point numbers -- something that's very common with GeoJSON.
```java
@Override
public void writeNumber(double d) throws IOException {
if (this._outputTail + DoubleToDecimal.MAX_CHARS >= this._outputEnd) {
this._flushBuffer();
}
this._outputTail = DoubleToDecimal.LATIN1.putDecimal(this._outputBuffer, this._outputTail, d);
}
```
A simple benchmark that tests this change by writing a 10k element float/double array to a null output stream shows that this approach is nearly 3x as fast, even when the relevant internal utility is copied out of the JDK (removing the requirement of `--add-opens`).
```
Benchmark (type) (mode) Mode Cnt Score Error Units
WriteFloatingPointArray.write FLOAT VANILLA thrpt 4 1934.454 ± 84.062 ops/s
WriteFloatingPointArray.write:gc.alloc.rate.norm FLOAT VANILLA thrpt 4 880296.792 ± 0.825 B/op
WriteFloatingPointArray.write FLOAT JACKSON_FAST thrpt 4 2116.445 ± 52.042 ops/s
WriteFloatingPointArray.write:gc.alloc.rate.norm FLOAT JACKSON_FAST thrpt 4 880263.591 ± 248.715 B/op
WriteFloatingPointArray.write FLOAT JDK_INTERNAL thrpt 4 3708.587 ± 491.341 ops/s
WriteFloatingPointArray.write:gc.alloc.rate.norm FLOAT JDK_INTERNAL thrpt 4 383.432 ± 12.262 B/op
WriteFloatingPointArray.write FLOAT JDK_COPY thrpt 4 3623.090 ± 240.293 ops/s
WriteFloatingPointArray.write:gc.alloc.rate.norm FLOAT JDK_COPY thrpt 4 383.387 ± 13.224 B/op
WriteFloatingPointArray.write DOUBLE VANILLA thrpt 4 1268.260 ± 39.709 ops/s
WriteFloatingPointArray.write:gc.alloc.rate.norm DOUBLE VANILLA thrpt 4 1040193.112 ± 0.036 B/op
WriteFloatingPointArray.write DOUBLE JACKSON_FAST thrpt 4 1532.169 ± 190.942 ops/s
WriteFloatingPointArray.write:gc.alloc.rate.norm DOUBLE JACKSON_FAST thrpt 4 1040192.921 ± 0.115 B/op
WriteFloatingPointArray.write DOUBLE JDK_INTERNAL thrpt 4 2788.184 ± 497.046 ops/s
WriteFloatingPointArray.write:gc.alloc.rate.norm DOUBLE JDK_INTERNAL thrpt 4 315.239 ± 35.377 B/op
WriteFloatingPointArray.write DOUBLE JDK_COPY thrpt 4 2704.880 ± 379.627 ops/s
WriteFloatingPointArray.write:gc.alloc.rate.norm DOUBLE JDK_COPY thrpt 4 315.170 ± 34.143 B/op
```
Based on these results it seems like it would be valuable to optimize Jackson's copy of `*ToDecimal` and bring back `USE_FAST_DOUBLE_WRITER` for modern JDKs? I'm not positive but I don't believe the JDK's implementation of these directly into jackson-core due to licensing, and I suspect relying on `--add-opens java.base/jdk.internal.math=ALL-UNNAMED` is not a good approach for a general purpose library.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start in UTF8JsonGenerator at writeNumber(double) and inspect the existing USE_FAST_DOUBLE_WRITER path. Compare the OpenJDK DoubleToDecimal and FloatToDecimal approach with Jackson's copied *ToDecimal utility, including the licensing concern, then run the mentioned WriteFloatingPointArray benchmark. Done means the proposed modern-JDK path is integrated with preserved floating-point output and its allocation and throughput results are verified.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- backend-api-design, performance
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100