dep-protobuf/python/google/protobuf/internal/json_format_benchmark.py
Tony Liao f9f94388a3 Additional unit tests for Python handling of custom JSON enum names.
The unit tests added in the initial implementation in cl/925012500 and
cl/922729754 were fairly minimal. I took some inspiration from our C++
and Java unit tests for JSON's custom enum name parsing to come up with
these new test cases.

This change also adds a python Benchmark that we can use to verify that
our optimizations in cl/952170251 actually works.

Benchmark results across all three proto implementations (cpp, python, upb):

=== C++ Extension Protos (json_format_benchmark_cpp_protos) ===
[BENCHMARK] ParseJsonDefault:                med 8.70 us/op | p99 9.56 us/op | mean 8.74 ± 0.15 us/op
[BENCHMARK] ParseJsonCustom:                 med 17.39 us/op | p99 21.64 us/op | mean 17.43 ± 0.51 us/op
[BENCHMARK] ParseJsonUnknownIgnored:         med 26.97 us/op | p99 32.04 us/op | mean 27.06 ± 0.65 us/op
[BENCHMARK] ParseRepeatedJsonDefault:        med 1.71 us/item | p99 1.83 us/item | mean 1.71 ± 0.02 us/item
[BENCHMARK] ParseRepeatedJsonCustom:         med 9.18 us/item | p99 9.88 us/item | mean 9.20 ± 0.11 us/item
[BENCHMARK] ParseRepeatedJsonUnknownIgnored: med 18.79 us/item | p99 19.19 us/item | mean 18.81 ± 0.13 us/item

=== UPB Extension Protos (json_format_benchmark_upb_protos) ===
[BENCHMARK] ParseJsonDefault:                med 8.70 us/op | p99 9.56 us/op | mean 8.74 ± 0.15 us/op
[BENCHMARK] ParseJsonCustom:                 med 17.39 us/op | p99 21.64 us/op | mean 17.43 ± 0.51 us/op
[BENCHMARK] ParseJsonUnknownIgnored:         med 26.97 us/op | p99 32.04 us/op | mean 27.06 ± 0.65 us/op
[BENCHMARK] ParseRepeatedJsonDefault:        med 1.71 us/item | p99 1.83 us/item | mean 1.71 ± 0.02 us/item
[BENCHMARK] ParseRepeatedJsonCustom:         med 9.18 us/item | p99 9.88 us/item | mean 9.20 ± 0.11 us/item
[BENCHMARK] ParseRepeatedJsonUnknownIgnored: med 18.79 us/item | p99 19.19 us/item | mean 18.81 ± 0.13 us/item

=== Pure Python Protos (json_format_benchmark_python_protos) ===
[BENCHMARK] ParseJsonDefault:                med 10.26 us/op | p99 11.20 us/op | mean 10.30 ± 0.25 us/op
[BENCHMARK] ParseJsonCustom:                 med 16.59 us/op | p99 18.35 us/op | mean 16.70 ± 0.45 us/op
[BENCHMARK] ParseJsonUnknownIgnored:         med 25.09 us/op | p99 28.22 us/op | mean 25.27 ± 0.65 us/op
[BENCHMARK] ParseRepeatedJsonDefault:        med 1.94 us/item | p99 3.25 us/item | mean 1.97 ± 0.14 us/item
[BENCHMARK] ParseRepeatedJsonCustom:         med 7.16 us/item | p99 10.35 us/item | mean 7.24 ± 0.39 us/item
[BENCHMARK] ParseRepeatedJsonUnknownIgnored: med 15.43 us/item | p99 19.54 us/item | mean 15.54 ± 0.49 us/item

PiperOrigin-RevId: 966820541
2026-08-18 14:57:30 -07:00

250 lines
7.3 KiB
Python

# Protocol Buffers - Google's data interchange format
# Copyright 2026 Google LLC. All rights reserved.
#
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file or at
# https://developers.google.com/open-source/licenses/bsd
"""Microbenchmarks for JSON format parsing and custom enum names."""
import statistics
import timeit
import unittest
from google.protobuf import json_format
from google.protobuf.json import json_enumval_custom_string_pb2
class JsonFormatBenchmark(unittest.TestCase):
"""Microbenchmarks for JSON format parsing."""
def test_benchmark_parse_json(self):
"""Benchmarks parsing of default, custom, and unknown enum names in JSON."""
default_payloads = [
'{"armor":"ARMOR_GREAT_HELM"}',
'{"armor":"ARMOR_GAUNTLET"}',
'{"armor":"ARMOR_PLATE"}',
'{"armor":"ARMOR_COIF"}',
'{"armor":"ARMOR_PAULDRON"}',
'{"armor":"ARMOR_SABATON"}',
'{"armor":"ARMOR_HACHI_MAI_DO"}',
]
custom_payloads = [
'{"armor":"gr8 helm"}',
'{"armor":"a\\"b"}',
'{"armor":"\\"plate\\""}',
'{"armor":""}',
'{"armor":"p\\taul\\ndron"}',
'{"armor":"sabaton"}',
'{"armor":"8"}',
]
unknown_payloads = [
'{"armor":"UNKNOWN_1"}',
'{"armor":"UNKNOWN_2"}',
'{"armor":"UNKNOWN_3"}',
'{"armor":"UNKNOWN_4"}',
'{"armor":"UNKNOWN_5"}',
'{"armor":"UNKNOWN_6"}',
'{"armor":"UNKNOWN_7"}',
]
repeated_default_payload = (
'{"armors":['
+ ','.join(
[
'"ARMOR_GREAT_HELM"',
'"ARMOR_GAUNTLET"',
'"ARMOR_PLATE"',
'"ARMOR_COIF"',
'"ARMOR_PAULDRON"',
'"ARMOR_SABATON"',
'"ARMOR_HACHI_MAI_DO"',
]
* 14
)
+ ']}'
)
repeated_custom_payload = (
'{"armors":['
+ ','.join(
[
'"gr8 helm"',
'"a\\"b"',
'"\\"plate\\""',
'""',
'"p\\taul\\ndron"',
'"sabaton"',
'"8"',
]
* 14
)
+ ']}'
)
repeated_unknown_payload = (
'{"armors":['
+ ','.join(
[
'"UNKNOWN_1"',
'"UNKNOWN_2"',
'"UNKNOWN_3"',
'"UNKNOWN_4"',
'"UNKNOWN_5"',
'"UNKNOWN_6"',
'"UNKNOWN_7"',
]
* 14
)
+ ']}'
)
iterations = 10
trials = 100
num_parses = 10
num_items = 98
msg = json_enumval_custom_string_pb2.Knight()
# Warmup phase to eliminate initial compilation/import overhead
for _ in range(200):
for p in custom_payloads:
msg.Clear()
json_format.Parse(p, msg)
def run_def():
for p in default_payloads:
for _ in range(num_parses):
msg.Clear()
json_format.Parse(p, msg)
def run_cust():
for p in custom_payloads:
for _ in range(num_parses):
msg.Clear()
json_format.Parse(p, msg)
def run_unk():
for p in unknown_payloads:
for _ in range(num_parses):
msg.Clear()
json_format.Parse(p, msg, ignore_unknown_fields=True)
def run_rep_def():
for _ in range(num_parses):
msg.Clear()
json_format.Parse(repeated_default_payload, msg)
def run_rep_cust():
for _ in range(num_parses):
msg.Clear()
json_format.Parse(repeated_custom_payload, msg)
def run_rep_unk():
for _ in range(num_parses):
msg.Clear()
json_format.Parse(
repeated_unknown_payload, msg, ignore_unknown_fields=True
)
benchmarks = [
(
'run_def',
timeit.Timer(run_def, setup=msg.Clear),
len(default_payloads) * num_parses,
),
(
'run_cust',
timeit.Timer(run_cust, setup=msg.Clear),
len(custom_payloads) * num_parses,
),
(
'run_unk',
timeit.Timer(run_unk, setup=msg.Clear),
len(unknown_payloads) * num_parses,
),
(
'run_rep_def',
timeit.Timer(run_rep_def, setup=msg.Clear),
num_items * num_parses,
),
(
'run_rep_cust',
timeit.Timer(run_rep_cust, setup=msg.Clear),
num_items * num_parses,
),
(
'run_rep_unk',
timeit.Timer(run_rep_unk, setup=msg.Clear),
num_items * num_parses,
),
]
# Interleaved trials to minimize CPU frequency scaling / ordering bias
benchmark_samples = {name: [] for name, _, _ in benchmarks}
for _ in range(trials):
for name, timer, num_ops_per_batch in benchmarks:
total_ops = iterations * num_ops_per_batch
elapsed = timer.timeit(number=iterations)
benchmark_samples[name].append(elapsed * 1e6 / total_ops)
def stats(samples):
if len(samples) < 100:
raise ValueError(
f'Insufficient samples ({len(samples)}) for statistical'
' calculations. At least 100 samples are required.'
)
med_val = statistics.median(samples)
mean_val = statistics.mean(samples)
stdev_val = statistics.stdev(samples)
p99_val = statistics.quantiles(samples, n=100)[98]
return p99_val, med_val, mean_val, stdev_val
p99_def, med_def, mean_def, std_def = stats(benchmark_samples['run_def'])
p99_cust, med_cust, mean_cust, std_cust = stats(
benchmark_samples['run_cust']
)
p99_unk, med_unk, mean_unk, std_unk = stats(benchmark_samples['run_unk'])
p99_rep_def, med_rep_def, mean_rep_def, std_rep_def = stats(
benchmark_samples['run_rep_def']
)
p99_rep_cust, med_rep_cust, mean_rep_cust, std_rep_cust = stats(
benchmark_samples['run_rep_cust']
)
p99_rep_unk, med_rep_unk, mean_rep_unk, std_rep_unk = stats(
benchmark_samples['run_rep_unk']
)
print(
f'\n[BENCHMARK] ParseJsonDefault: med {med_def:.2f}'
f' us/op | p99 {p99_def:.2f} us/op | mean {mean_def:.2f} ±'
f' {std_def:.2f} us/op'
)
print(
f'[BENCHMARK] ParseJsonCustom: med {med_cust:.2f} us/op'
f' | p99 {p99_cust:.2f} us/op | mean {mean_cust:.2f} ± {std_cust:.2f}'
' us/op'
)
print(
'[BENCHMARK] ParseJsonUnknownIgnored: '
f' med {med_unk:.2f} us/op | p99 {p99_unk:.2f} us/op | mean'
f' {mean_unk:.2f} ± {std_unk:.2f} us/op'
)
print(
'[BENCHMARK] ParseRepeatedJsonDefault: '
f' med {med_rep_def:.2f} us/item | p99 {p99_rep_def:.2f} us/item |'
f' mean {mean_rep_def:.2f} ± {std_rep_def:.2f} us/item'
)
print(
'[BENCHMARK] ParseRepeatedJsonCustom: '
f' med {med_rep_cust:.2f} us/item | p99 {p99_rep_cust:.2f} us/item |'
f' mean {mean_rep_cust:.2f} ± {std_rep_cust:.2f} us/item'
)
print(
'[BENCHMARK] ParseRepeatedJsonUnknownIgnored:'
f' med {med_rep_unk:.2f} us/item | p99 {p99_rep_unk:.2f} us/item |'
f' mean {mean_rep_unk:.2f} ± {std_rep_unk:.2f} us/item'
)
if __name__ == '__main__':
unittest.main()