Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -812,6 +812,25 @@ optimized for maximum performance across JSON encoding, decoding, and Java
object mapping. It supports Java 8 and later on standard JDKs, GraalVM native
images, and Android, with Java records supported on Java 17 and later.

**Performance**

The benchmark compares fory-json with Jackson and Gson using String and UTF-8 byte APIs. The String
group excludes UTF-8 conversion. Gson's byte results include its required String/UTF-8 conversion.
Higher throughput is better. See the [full benchmark report](docs/benchmarks/java/json/) for more
details.

<p align="center">
<img src="docs/benchmarks/java/json/string_throughput.png" width="49%" alt="Java JSON String throughput">
<img src="docs/benchmarks/java/json/utf8_bytes_throughput.png" width="49%" alt="Java JSON UTF-8 bytes throughput">
</p>

| Representation | Operation | fory-json ops/sec | Jackson ops/sec | Gson ops/sec |
| -------------- | ----------- | ----------------: | --------------: | -----------: |
| String | Serialize | 7,387,465 | 2,049,368 | 1,084,042 |
| String | Deserialize | 2,897,955 | 1,074,885 | 902,772 |
| UTF-8 bytes | Serialize | 10,375,498 | 1,868,614 | 1,037,211 |
| UTF-8 bytes | Deserialize | 3,077,158 | 1,268,397 | 933,079 |

Add Fory JSON to your project:

**Maven**
Expand Down
31 changes: 24 additions & 7 deletions benchmarks/java/benchmark_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
from matplotlib.ticker import FuncFormatter

sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from plot_style import ( # noqa: E402
from plot_style import (
BAR_EDGE_COLOR,
GROUP_BAR_WIDTH,
GROUP_X,
Expand Down Expand Up @@ -66,6 +66,8 @@
"mediacontentlist",
]
OPERATIONS = ["serialize", "deserialize"]
DOCS_HEADER = "# Java Benchmarks"
JSON_REPORT_LINK = "See the [Java JSON benchmark report](json/) for fory-json, Jackson, and Gson results."
BENCHMARK_PATTERN = re.compile(
r"(?:^|[.])BM_(?P<serializer>Fory|Protobuf|Flatbuffer)_"
r"(?P<datatype>NumericStruct|Sample|MediaContent|NumericStructList|SampleList|MediaContentList)_"
Expand Down Expand Up @@ -251,15 +253,19 @@ def winner_cell(values: dict) -> str:
def build_xlang_section(results: dict, image_name: str) -> str:
lines = [
"## Xlang Benchmark\n\n",
"Run from `benchmarks/java/run.sh`. Raw JMH JSON stays under the ignored local "
"`benchmarks/java/reports/` directory; `throughput.png` and this xlang "
"section are synced into `docs/benchmarks/java/`.\n\n",
(
"Run from `benchmarks/java/run.sh`. Raw JMH JSON stays under the ignored local "
"`benchmarks/java/reports/` directory; `throughput.png` and this xlang "
"section are synced into `docs/benchmarks/java/`.\n\n"
),
"```bash\n",
"cd benchmarks/java\n",
"./run.sh\n",
"```\n\n",
"JMH parameters: `-f 1 -wi 3 -i 3 -t 1 -w 3s -r 3s -bm thrpt -tu s`. "
"Higher throughput is better.\n\n",
(
"JMH parameters: `-f 1 -wi 3 -i 3 -t 1 -w 3s -r 3s -bm thrpt -tu s`. "
"Higher throughput is better.\n\n"
),
f"![Java Xlang Serialization Throughput]({image_name})\n\n",
"| Data type | Operation | "
+ " | ".join(
Expand Down Expand Up @@ -303,14 +309,25 @@ def update_docs_readme(docs_output_dir: Path, section: str) -> Path:
docs_readme = docs_output_dir / "README.md"
if docs_readme.exists():
content = docs_readme.read_text(encoding="utf-8").rstrip()
if JSON_REPORT_LINK not in content:
if content.startswith(DOCS_HEADER):
content = (
DOCS_HEADER
+ "\n\n"
+ JSON_REPORT_LINK
+ "\n\n"
+ content[len(DOCS_HEADER) :].lstrip()
)
else:
content = JSON_REPORT_LINK + "\n\n" + content
marker = "\n## Xlang Benchmark\n"
if marker in content:
prefix = content.split(marker, 1)[0].rstrip()
content = prefix + "\n\n" + section
else:
content = content + "\n\n" + section
else:
content = "# Java Benchmarks\n\n" + section
content = DOCS_HEADER + "\n\n" + JSON_REPORT_LINK + "\n\n" + section
docs_readme.write_text(content.rstrip() + "\n", encoding="utf-8")
run_prettier(docs_readme)
return docs_readme
Expand Down
8 changes: 4 additions & 4 deletions benchmarks/java/plot_json_benchmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
from matplotlib.ticker import FuncFormatter

sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from plot_style import ( # noqa: E402
from plot_style import (
BAR_EDGE_COLOR,
apply_benchmark_style,
format_markdown_with_prettier,
Expand Down Expand Up @@ -112,7 +112,7 @@ def parse_libs(value: str) -> tuple[str, ...]:
for name in requested:
serializer = LIB_ALIASES.get(name)
if serializer is None:
choices = ", ".join(("fory-json", "jackson", "gson", "fastjson2"))
choices = "fory-json, jackson, gson, fastjson2"
raise ValueError(f"Unknown JSON library: {name}. Available: {choices}")
if serializer in serializers:
raise ValueError(f"Duplicate JSON library: {SERIALIZER_LABELS[serializer]}")
Expand All @@ -125,7 +125,7 @@ def load_json(path: Path) -> list[dict[str, Any]]:
payload = json.load(source)
benchmarks = payload if isinstance(payload, list) else payload.get("benchmarks", [])
if not isinstance(benchmarks, list):
raise ValueError(f"Expected a JMH benchmark list in {path}")
raise TypeError(f"Expected a JMH benchmark list in {path}")
return benchmarks


Expand Down Expand Up @@ -267,7 +267,7 @@ def render_report(
library_names = ", ".join(SERIALIZER_LABELS[name] for name in serializers)
lines = [
"# Java JSON Benchmark Report\n\n",
f"The benchmark compares {library_names} using the same MediaContent data. "
f"The benchmark compares {library_names} using the same data. "
"The String group excludes UTF-8 conversion. The UTF-8 bytes group uses "
"direct byte-array APIs when available. "
+ (
Expand Down
2 changes: 2 additions & 0 deletions docs/benchmarks/java/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Java Benchmarks

See the [Java JSON benchmark report](json/) for fory-json, Jackson, and Gson results.

## System Environment

- Operation System:4.9.151-015.x86_64
Expand Down
38 changes: 38 additions & 0 deletions docs/benchmarks/java/json/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Java JSON Benchmark Report

The benchmark compares fory-json, Jackson, and Gson using the same data. The String group excludes
UTF-8 conversion. The UTF-8 bytes group uses direct byte-array APIs when available. Gson includes
String-to-UTF-8 encoding and UTF-8-to-String decoding.

```bash
cd benchmarks/java
./run_json.sh --libs fory-json,jackson,gson
```

- Benchmark date: `2026-07-28`
- Source commit: `bd0ca9825b3206b1a53bf27bfb180a551ab39236`
- Platform: Apple M4 Pro, macOS 15.7.2, arm64
- JDK: `26.0.1`
- VM: `OpenJDK 64-Bit Server VM`
- JMH: `1.37`
- Warmup: 3 iterations × `2 s`
- Measurement: 5 iterations × `2 s`
- Forks: 1; threads: 1
- Mode: throughput; higher is better

## String

![Java JSON String benchmark throughput](string_throughput.png)

## UTF-8 Bytes

![Java JSON UTF-8 bytes benchmark throughput](utf8_bytes_throughput.png)

## Results

| Representation | Operation | fory-json ops/sec | jackson ops/sec | gson ops/sec | Fastest |
| -------------- | ----------- | ----------------: | --------------: | -----------: | --------- |
| String | Serialize | 7,387,465 | 2,049,368 | 1,084,042 | fory-json |
| String | Deserialize | 2,897,955 | 1,074,885 | 902,772 | fory-json |
| UTF-8 bytes | Serialize | 10,375,498 | 1,868,614 | 1,037,211 | fory-json |
| UTF-8 bytes | Deserialize | 3,077,158 | 1,268,397 | 933,079 | fory-json |
Binary file added docs/benchmarks/java/json/string_throughput.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 20 additions & 0 deletions java/fory-json/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,26 @@ system must exchange ordinary JSON with browsers, APIs, logs, configuration, or
implementation. Use the Fory binary protocol when you need cross-language schema metadata,
reference identity, circular graphs, or Fory's binary-only features.

## Performance

The benchmark compares fory-json with Jackson and Gson using String and UTF-8 byte APIs. The String
group excludes UTF-8 conversion. Gson's byte results include its required String/UTF-8 conversion.
Higher throughput is better.

<p align="center">
<img src="../../docs/benchmarks/java/json/string_throughput.png" width="49%" alt="Java JSON String throughput">
<img src="../../docs/benchmarks/java/json/utf8_bytes_throughput.png" width="49%" alt="Java JSON UTF-8 bytes throughput">
</p>

| Representation | Operation | fory-json ops/sec | Jackson ops/sec | Gson ops/sec |
| -------------- | ----------- | ----------------: | --------------: | -----------: |
| String | Serialize | 7,387,465 | 2,049,368 | 1,084,042 |
| String | Deserialize | 2,897,955 | 1,074,885 | 902,772 |
| UTF-8 bytes | Serialize | 10,375,498 | 1,868,614 | 1,037,211 |
| UTF-8 bytes | Deserialize | 3,077,158 | 1,268,397 | 933,079 |

See the [full benchmark report](../../docs/benchmarks/java/json/).

## Requirements and installation

Fory JSON supports Java 8 and later on standard JDKs, GraalVM native images, and Android. Java
Expand Down
Loading