This is an automated email from the ASF dual-hosted git repository.
chaokunyang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fory.git
The following commit(s) were added to refs/heads/main by this push:
new 402a12e14 perf(c++): add media content benchmark (#2968)
402a12e14 is described below
commit 402a12e1401664b02f602399361ab25b5d648024
Author: Shawn Yang <[email protected]>
AuthorDate: Wed Dec 3 17:01:59 2025 +0800
perf(c++): add media content benchmark (#2968)
## Why?
<!-- Describe the purpose of this PR. -->
## What does this PR do?
add media content benchmark
## Related issues
#2958
## Does this PR introduce any user-facing change?
<!--
If any user-facing interface changes, please [open an
issue](https://github.com/apache/fory/issues/new/choose) describing the
need to do so and update the document if necessary.
Delete section if not applicable.
-->
- [ ] Does this PR introduce any public API change?
- [ ] Does this PR introduce any binary protocol compatibility change?
## Benchmark
| Benchmark | Fory | Protobuf | Speedup |
|-------------|-----------|-----------|--------------|
| Serialize | 560ns | 2004ns | ~3.6x faster |
| Deserialize | 1975ns | 2533ns | ~1.3x faster |
| Size | 310 bytes | 301 bytes | similar |
---
benchmarks/cpp_benchmark/benchmark.cc | 267 +++++++++++++++++++++++++++++++++-
1 file changed, 266 insertions(+), 1 deletion(-)
diff --git a/benchmarks/cpp_benchmark/benchmark.cc
b/benchmarks/cpp_benchmark/benchmark.cc
index fa3ec009b..c9767ba9b 100644
--- a/benchmarks/cpp_benchmark/benchmark.cc
+++ b/benchmarks/cpp_benchmark/benchmark.cc
@@ -100,6 +100,61 @@ FORY_STRUCT(Sample, int_value, long_value, float_value,
double_value,
long_array, float_array, double_array, short_array, char_array,
boolean_array, string);
+// Enums for MediaContent benchmark
+enum class Player : int32_t { JAVA = 0, FLASH = 1 };
+
+enum class Size : int32_t { SMALL = 0, LARGE = 1 };
+
+struct Media {
+ std::string uri;
+ std::string title; // Can be empty (null equivalent)
+ int32_t width;
+ int32_t height;
+ std::string format;
+ int64_t duration;
+ int64_t size;
+ int32_t bitrate;
+ bool has_bitrate;
+ std::vector<std::string> persons;
+ Player player;
+ std::string copyright;
+
+ bool operator==(const Media &other) const {
+ return uri == other.uri && title == other.title && width == other.width &&
+ height == other.height && format == other.format &&
+ duration == other.duration && size == other.size &&
+ bitrate == other.bitrate && has_bitrate == other.has_bitrate &&
+ persons == other.persons && player == other.player &&
+ copyright == other.copyright;
+ }
+};
+FORY_STRUCT(Media, uri, title, width, height, format, duration, size, bitrate,
+ has_bitrate, persons, player, copyright);
+
+struct Image {
+ std::string uri;
+ std::string title; // Can be empty (null equivalent)
+ int32_t width;
+ int32_t height;
+ Size size;
+
+ bool operator==(const Image &other) const {
+ return uri == other.uri && title == other.title && width == other.width &&
+ height == other.height && size == other.size;
+ }
+};
+FORY_STRUCT(Image, uri, title, width, height, size);
+
+struct MediaContent {
+ Media media;
+ std::vector<Image> images;
+
+ bool operator==(const MediaContent &other) const {
+ return media == other.media && images == other.images;
+ }
+};
+FORY_STRUCT(MediaContent, media, images);
+
// ============================================================================
// Test data creation
// ============================================================================
@@ -237,6 +292,124 @@ protobuf::Sample CreateProtoSample() {
return sample;
}
+MediaContent CreateMediaContent() {
+ // Matches Java MediaContent.populate(false) - no circular reference
+ MediaContent content;
+
+ // Media fields matching Java populate()
+ content.media.uri = "http://javaone.com/keynote.ogg";
+ content.media.title = ""; // null in Java
+ content.media.width = 641;
+ content.media.height = 481;
+ content.media.format = u8"video/theora\u1234"; // UTF-8 encoded unicode
+ content.media.duration = 18000001;
+ content.media.size = 58982401;
+ content.media.bitrate = 0;
+ content.media.has_bitrate = false;
+ content.media.persons = {"Bill Gates, Jr.", "Steven Jobs"};
+ content.media.player = Player::FLASH;
+ content.media.copyright = "Copyright (c) 2009, Scooby Dooby Doo";
+
+ // Images matching Java populate(false) - no circular reference
+ content.images = {
+ Image{"http://javaone.com/keynote_huge.jpg", u8"Javaone Keynote\u1234",
+ 32000, 24000, Size::LARGE},
+ Image{"http://javaone.com/keynote_large.jpg", "", 1024, 768,
Size::LARGE},
+ Image{"http://javaone.com/keynote_small.jpg", "", 320, 240,
Size::SMALL}};
+
+ return content;
+}
+
+/// Convert Image to protobuf
+inline protobuf::Image ToPbImage(const Image &img) {
+ protobuf::Image pb;
+ pb.set_uri(img.uri);
+ if (!img.title.empty()) {
+ pb.set_title(img.title);
+ }
+ pb.set_width(img.width);
+ pb.set_height(img.height);
+ pb.set_size(static_cast<protobuf::Size>(img.size));
+ return pb;
+}
+
+/// Convert Media to protobuf
+inline protobuf::Media ToPbMedia(const Media &m) {
+ protobuf::Media pb;
+ pb.set_uri(m.uri);
+ if (!m.title.empty()) {
+ pb.set_title(m.title);
+ }
+ pb.set_width(m.width);
+ pb.set_height(m.height);
+ pb.set_format(m.format);
+ pb.set_duration(m.duration);
+ pb.set_size(m.size);
+ pb.set_bitrate(m.bitrate);
+ pb.set_has_bitrate(m.has_bitrate);
+ for (const auto &person : m.persons) {
+ pb.add_persons(person);
+ }
+ pb.set_player(static_cast<protobuf::Player>(m.player));
+ pb.set_copyright(m.copyright);
+ return pb;
+}
+
+/// Convert MediaContent to protobuf
+inline protobuf::MediaContent ToPbMediaContent(const MediaContent &mc) {
+ protobuf::MediaContent pb;
+ *pb.mutable_media() = ToPbMedia(mc.media);
+ for (const auto &img : mc.images) {
+ *pb.add_images() = ToPbImage(img);
+ }
+ return pb;
+}
+
+/// Convert protobuf to Image
+inline Image FromPbImage(const protobuf::Image &pb) {
+ Image img;
+ img.uri = pb.uri();
+ img.title = pb.has_title() ? pb.title() : "";
+ img.width = pb.width();
+ img.height = pb.height();
+ img.size = static_cast<Size>(pb.size());
+ return img;
+}
+
+/// Convert protobuf to Media
+inline Media FromPbMedia(const protobuf::Media &pb) {
+ Media m;
+ m.uri = pb.uri();
+ m.title = pb.has_title() ? pb.title() : "";
+ m.width = pb.width();
+ m.height = pb.height();
+ m.format = pb.format();
+ m.duration = pb.duration();
+ m.size = pb.size();
+ m.bitrate = pb.bitrate();
+ m.has_bitrate = pb.has_bitrate();
+ for (const auto &person : pb.persons()) {
+ m.persons.push_back(person);
+ }
+ m.player = static_cast<Player>(pb.player());
+ m.copyright = pb.copyright();
+ return m;
+}
+
+/// Convert protobuf to MediaContent
+inline MediaContent FromPbMediaContent(const protobuf::MediaContent &pb) {
+ MediaContent mc;
+ mc.media = FromPbMedia(pb.media());
+ for (const auto &img : pb.images()) {
+ mc.images.push_back(FromPbImage(img));
+ }
+ return mc;
+}
+
+protobuf::MediaContent CreateProtoMediaContent() {
+ return ToPbMediaContent(CreateMediaContent());
+}
+
// ============================================================================
// Helper to configure Fory instance
// ============================================================================
@@ -244,6 +417,9 @@ protobuf::Sample CreateProtoSample() {
void RegisterForyTypes(fory::serialization::Fory &fory) {
fory.register_struct<NumericStruct>(1);
fory.register_struct<Sample>(2);
+ fory.register_struct<Media>(3);
+ fory.register_struct<Image>(4);
+ fory.register_struct<MediaContent>(5);
}
// ============================================================================
@@ -413,6 +589,89 @@ static void
BM_Protobuf_Sample_Deserialize(benchmark::State &state) {
}
BENCHMARK(BM_Protobuf_Sample_Deserialize);
+// ============================================================================
+// MediaContent benchmarks (nested objects with strings and lists)
+// ============================================================================
+
+static void BM_Fory_MediaContent_Serialize(benchmark::State &state) {
+ auto fory = fory::serialization::Fory::builder()
+ .xlang(true)
+ .track_ref(false)
+ .check_struct_version(false)
+ .build();
+ RegisterForyTypes(fory);
+ MediaContent obj = CreateMediaContent();
+
+ // Pre-allocate buffer
+ fory::Buffer buffer;
+ buffer.Reserve(4096);
+
+ for (auto _ : state) {
+ buffer.WriterIndex(0);
+ auto result = fory.serialize_to(obj, buffer);
+ benchmark::DoNotOptimize(result);
+ benchmark::DoNotOptimize(buffer.data());
+ }
+}
+BENCHMARK(BM_Fory_MediaContent_Serialize);
+
+static void BM_Protobuf_MediaContent_Serialize(benchmark::State &state) {
+ MediaContent obj = CreateMediaContent();
+ protobuf::MediaContent pb = ToPbMediaContent(obj);
+ std::vector<uint8_t> output;
+ output.resize(pb.ByteSizeLong());
+
+ for (auto _ : state) {
+ pb = ToPbMediaContent(obj);
+ pb.SerializeToArray(output.data(), static_cast<int>(output.size()));
+ benchmark::DoNotOptimize(output);
+ }
+}
+BENCHMARK(BM_Protobuf_MediaContent_Serialize);
+
+static void BM_Fory_MediaContent_Deserialize(benchmark::State &state) {
+ auto fory = fory::serialization::Fory::builder()
+ .xlang(true)
+ .track_ref(false)
+ .check_struct_version(false)
+ .build();
+ RegisterForyTypes(fory);
+ MediaContent obj = CreateMediaContent();
+ auto serialized = fory.serialize(obj);
+ if (!serialized.ok()) {
+ state.SkipWithError("Serialization failed");
+ return;
+ }
+ auto &bytes = serialized.value();
+
+ // Verify deserialization works first
+ auto test_result = fory.deserialize<MediaContent>(bytes.data(),
bytes.size());
+ if (!test_result.ok()) {
+ state.SkipWithError("Deserialization test failed");
+ return;
+ }
+
+ for (auto _ : state) {
+ auto result = fory.deserialize<MediaContent>(bytes.data(), bytes.size());
+ benchmark::DoNotOptimize(result);
+ }
+}
+BENCHMARK(BM_Fory_MediaContent_Deserialize);
+
+static void BM_Protobuf_MediaContent_Deserialize(benchmark::State &state) {
+ protobuf::MediaContent obj = CreateProtoMediaContent();
+ std::string serialized;
+ obj.SerializeToString(&serialized);
+
+ for (auto _ : state) {
+ protobuf::MediaContent pb_result;
+ pb_result.ParseFromString(serialized);
+ MediaContent result = FromPbMediaContent(pb_result);
+ benchmark::DoNotOptimize(result);
+ }
+}
+BENCHMARK(BM_Protobuf_MediaContent_Deserialize);
+
// ============================================================================
// Serialized size comparison (printed once at the end)
// ============================================================================
@@ -427,15 +686,19 @@ static void BM_PrintSerializedSizes(benchmark::State
&state) {
RegisterForyTypes(fory);
NumericStruct fory_struct = CreateNumericStruct();
Sample fory_sample = CreateSample();
+ MediaContent fory_media = CreateMediaContent();
auto fory_struct_bytes = fory.serialize(fory_struct).value();
auto fory_sample_bytes = fory.serialize(fory_sample).value();
+ auto fory_media_bytes = fory.serialize(fory_media).value();
// Protobuf
protobuf::Struct proto_struct = CreateProtoStruct();
protobuf::Sample proto_sample = CreateProtoSample();
- std::string proto_struct_bytes, proto_sample_bytes;
+ protobuf::MediaContent proto_media = CreateProtoMediaContent();
+ std::string proto_struct_bytes, proto_sample_bytes, proto_media_bytes;
proto_struct.SerializeToString(&proto_struct_bytes);
proto_sample.SerializeToString(&proto_sample_bytes);
+ proto_media.SerializeToString(&proto_media_bytes);
for (auto _ : state) {
// Just run once to print sizes
@@ -445,5 +708,7 @@ static void BM_PrintSerializedSizes(benchmark::State
&state) {
state.counters["proto_struct_size"] = proto_struct_bytes.size();
state.counters["fory_sample_size"] = fory_sample_bytes.size();
state.counters["proto_sample_size"] = proto_sample_bytes.size();
+ state.counters["fory_media_size"] = fory_media_bytes.size();
+ state.counters["proto_media_size"] = proto_media_bytes.size();
}
BENCHMARK(BM_PrintSerializedSizes)->Iterations(1);
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]