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 6f5974822 perf: optimize schema evolution mode performance (#3260)
6f5974822 is described below

commit 6f5974822db4a9396957bac8cc01b77d1e00025c
Author: Shawn Yang <[email protected]>
AuthorDate: Thu Feb 5 15:54:14 2026 +0800

    perf: optimize schema evolution mode performance (#3260)
    
    ## Why?
    
    Improve schema evolution/compatible mode throughput by reducing
    type-meta lookup overhead and enabling fast-path reads when schemas
    match, and extend benchmarks to cover list workloads.
    
    ## What does this PR do?
    
    - C++: optimize compatible-mode type-meta sharing/caching and add a
    fast-path struct read when schema hashes match; align benchmarks with
    field tags and compatible mode.
    - Go: speed up compatible-mode type-meta sharing (uintptr key +
    first-type fast path), cache TypeInfo for struct/pointer fields and
    typedefs, and extend benchmarks/reporting for list workloads.
    - Rust: add schema hash and fast type/meta lookup caches, use a faster
    vec reader and type-info path, and tag benchmark models + add a
    benchmark runner script.
    
    ## Related issues
    
    #1017
    #2906
    #2982
    
    ## Does this PR introduce any user-facing change?
    
    
    
    - [ ] Does this PR introduce any public API change?
    - [ ] Does this PR introduce any binary protocol compatibility change?
    
    ## Benchmark
---
 .gitignore                                         |   2 +
 benchmarks/cpp_benchmark/benchmark.cc              | 506 +++++++++++++++++++--
 benchmarks/cpp_benchmark/benchmark_report.py       |  39 +-
 benchmarks/go_benchmark/README.md                  |  18 +-
 benchmarks/go_benchmark/benchmark_report.py        | 107 ++++-
 benchmarks/go_benchmark/benchmark_test.go          | 334 ++++++++++++++
 benchmarks/go_benchmark/models.go                  | 134 ++++--
 benchmarks/go_benchmark/proto_convert.go           |  63 +++
 benchmarks/go_benchmark/run.sh                     |  15 +-
 benchmarks/proto/bench.proto                       |   8 +
 .../rust_benchmark/benches/threaded_bench.rs       |   9 +
 benchmarks/rust_benchmark/run.sh                   | 202 ++++++++
 benchmarks/rust_benchmark/src/models/complex.rs    |  26 ++
 benchmarks/rust_benchmark/src/models/medium.rs     |  14 +
 benchmarks/rust_benchmark/src/models/realworld.rs  |  25 +
 benchmarks/rust_benchmark/src/models/simple.rs     |   8 +
 cpp/fory/serialization/context.cc                  | 105 ++++-
 cpp/fory/serialization/context.h                   |  17 +-
 cpp/fory/serialization/struct_serializer.h         | 127 +++++-
 cpp/fory/serialization/struct_test.cc              |  48 ++
 cpp/fory/serialization/type_resolver.cc            |  20 +-
 cpp/fory/serialization/type_resolver.h             |   6 +
 cpp/fory/serialization/xlang_test_main.cc          |  19 -
 go/fory/field_info.go                              |   5 +-
 go/fory/fory.go                                    |   2 +-
 go/fory/pointer.go                                 |  41 ++
 go/fory/struct.go                                  |  52 +++
 go/fory/struct_init.go                             |  40 +-
 go/fory/type_def.go                                |  13 +
 go/fory/type_resolver.go                           | 123 +++--
 rust/fory-core/src/error.rs                        |  17 +-
 rust/fory-core/src/meta/type_meta.rs               |  56 +++
 rust/fory-core/src/resolver/meta_resolver.rs       |  15 +
 rust/fory-core/src/resolver/type_resolver.rs       | 109 ++++-
 rust/fory-core/src/serializer/collection.rs        |  91 ++++
 rust/fory-core/src/serializer/list.rs              |   4 +-
 rust/fory-core/src/serializer/struct_.rs           |  33 +-
 rust/fory-derive/src/object/read.rs                |  41 +-
 38 files changed, 2275 insertions(+), 219 deletions(-)

diff --git a/.gitignore b/.gitignore
index 61c8731f9..f36ebadb6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -104,3 +104,5 @@ examples/cpp/cmake_example/build
 **/results/
 benchmarks/**/report/
 ignored/**
+ci-logs/**
+**/*.log
\ No newline at end of file
diff --git a/benchmarks/cpp_benchmark/benchmark.cc 
b/benchmarks/cpp_benchmark/benchmark.cc
index 4972a5462..3cfa461c2 100644
--- a/benchmarks/cpp_benchmark/benchmark.cc
+++ b/benchmarks/cpp_benchmark/benchmark.cc
@@ -48,6 +48,8 @@ struct NumericStruct {
   }
 };
 FORY_STRUCT(NumericStruct, f1, f2, f3, f4, f5, f6, f7, f8);
+FORY_FIELD_TAGS(NumericStruct, (f1, 1), (f2, 2), (f3, 3), (f4, 4), (f5, 5),
+                (f6, 6), (f7, 7), (f8, 8));
 
 struct Sample {
   int32_t int_value;
@@ -99,6 +101,14 @@ FORY_STRUCT(Sample, int_value, long_value, float_value, 
double_value,
             short_value_boxed, char_value_boxed, boolean_value_boxed, 
int_array,
             long_array, float_array, double_array, short_array, char_array,
             boolean_array, string);
+FORY_FIELD_TAGS(Sample, (int_value, 1), (long_value, 2), (float_value, 3),
+                (double_value, 4), (short_value, 5), (char_value, 6),
+                (boolean_value, 7), (int_value_boxed, 8), (long_value_boxed, 
9),
+                (float_value_boxed, 10), (double_value_boxed, 11),
+                (short_value_boxed, 12), (char_value_boxed, 13),
+                (boolean_value_boxed, 14), (int_array, 15), (long_array, 16),
+                (float_array, 17), (double_array, 18), (short_array, 19),
+                (char_array, 20), (boolean_array, 21), (string, 22));
 
 // Enums for MediaContent benchmark
 enum class Player : int32_t { JAVA = 0, FLASH = 1 };
@@ -130,6 +140,9 @@ struct Media {
 };
 FORY_STRUCT(Media, uri, title, width, height, format, duration, size, bitrate,
             has_bitrate, persons, player, copyright);
+FORY_FIELD_TAGS(Media, (uri, 1), (title, 2), (width, 3), (height, 4),
+                (format, 5), (duration, 6), (size, 7), (bitrate, 8),
+                (has_bitrate, 9), (persons, 10), (player, 11), (copyright, 
12));
 
 struct Image {
   std::string uri;
@@ -144,6 +157,8 @@ struct Image {
   }
 };
 FORY_STRUCT(Image, uri, title, width, height, size);
+FORY_FIELD_TAGS(Image, (uri, 1), (title, 2), (width, 3), (height, 4),
+                (size, 5));
 
 struct MediaContent {
   Media media;
@@ -154,6 +169,37 @@ struct MediaContent {
   }
 };
 FORY_STRUCT(MediaContent, media, images);
+FORY_FIELD_TAGS(MediaContent, (media, 1), (images, 2));
+
+struct StructList {
+  std::vector<NumericStruct> struct_list;
+
+  bool operator==(const StructList &other) const {
+    return struct_list == other.struct_list;
+  }
+};
+FORY_STRUCT(StructList, struct_list);
+FORY_FIELD_TAGS(StructList, (struct_list, 1));
+
+struct SampleList {
+  std::vector<Sample> sample_list;
+
+  bool operator==(const SampleList &other) const {
+    return sample_list == other.sample_list;
+  }
+};
+FORY_STRUCT(SampleList, sample_list);
+FORY_FIELD_TAGS(SampleList, (sample_list, 1));
+
+struct MediaContentList {
+  std::vector<MediaContent> media_content_list;
+
+  bool operator==(const MediaContentList &other) const {
+    return media_content_list == other.media_content_list;
+  }
+};
+FORY_STRUCT(MediaContentList, media_content_list);
+FORY_FIELD_TAGS(MediaContentList, (media_content_list, 1));
 
 // ============================================================================
 // Test data creation
@@ -173,6 +219,8 @@ NumericStruct create_numeric_struct() {
   };
 }
 
+constexpr int kListSize = 5;
+
 // ============================================================================
 // Protobuf conversion functions (like Java benchmark's
 // buildPBStruct/fromPBObject)
@@ -245,53 +293,85 @@ Sample create_sample() {
   return sample;
 }
 
-protobuf::Sample create_proto_sample() {
-  // Consistent with Java Sample.populate() for fair cross-language comparison
+inline protobuf::Sample to_pb_sample(const Sample &obj) {
   protobuf::Sample sample;
-  sample.set_int_value(123);
-  sample.set_long_value(1230000LL);
-  sample.set_float_value(12.345f);
-  sample.set_double_value(1.234567);
-  sample.set_short_value(12345);
-  sample.set_char_value('!'); // 33
-  sample.set_boolean_value(true);
-
-  sample.set_int_value_boxed(321);
-  sample.set_long_value_boxed(3210000LL);
-  sample.set_float_value_boxed(54.321f);
-  sample.set_double_value_boxed(7.654321);
-  sample.set_short_value_boxed(32100);
-  sample.set_char_value_boxed('$'); // 36
-  sample.set_boolean_value_boxed(false);
-
-  // Arrays with mixed positive/negative values (same as Java)
-  for (int v : {-1234, -123, -12, -1, 0, 1, 12, 123, 1234}) {
+  sample.set_int_value(obj.int_value);
+  sample.set_long_value(obj.long_value);
+  sample.set_float_value(obj.float_value);
+  sample.set_double_value(obj.double_value);
+  sample.set_short_value(obj.short_value);
+  sample.set_char_value(obj.char_value);
+  sample.set_boolean_value(obj.boolean_value);
+
+  sample.set_int_value_boxed(obj.int_value_boxed);
+  sample.set_long_value_boxed(obj.long_value_boxed);
+  sample.set_float_value_boxed(obj.float_value_boxed);
+  sample.set_double_value_boxed(obj.double_value_boxed);
+  sample.set_short_value_boxed(obj.short_value_boxed);
+  sample.set_char_value_boxed(obj.char_value_boxed);
+  sample.set_boolean_value_boxed(obj.boolean_value_boxed);
+
+  for (int32_t v : obj.int_array) {
     sample.add_int_array(v);
   }
-  for (int64_t v : {-123400LL, -12300LL, -1200LL, -100LL, 0LL, 100LL, 1200LL,
-                    12300LL, 123400LL}) {
+  for (int64_t v : obj.long_array) {
     sample.add_long_array(v);
   }
-  for (float v :
-       {-12.34f, -12.3f, -12.0f, -1.0f, 0.0f, 1.0f, 12.0f, 12.3f, 12.34f}) {
+  for (float v : obj.float_array) {
     sample.add_float_array(v);
   }
-  for (double v : {-1.234, -1.23, -12.0, -1.0, 0.0, 1.0, 12.0, 1.23, 1.234}) {
+  for (double v : obj.double_array) {
     sample.add_double_array(v);
   }
-  for (int v : {-1234, -123, -12, -1, 0, 1, 12, 123, 1234}) {
+  for (int32_t v : obj.short_array) {
     sample.add_short_array(v);
   }
-  for (int v : {'a', 's', 'd', 'f', 'A', 'S', 'D', 'F'}) { // "asdfASDF"
+  for (int32_t v : obj.char_array) {
     sample.add_char_array(v);
   }
-  for (bool v : {true, false, false, true}) {
+  for (bool v : obj.boolean_array) {
     sample.add_boolean_array(v);
   }
-  sample.set_string("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
+  sample.set_string(obj.string);
+  return sample;
+}
+
+inline Sample from_pb_sample(const protobuf::Sample &pb) {
+  Sample sample;
+  sample.int_value = pb.int_value();
+  sample.long_value = pb.long_value();
+  sample.float_value = pb.float_value();
+  sample.double_value = pb.double_value();
+  sample.short_value = pb.short_value();
+  sample.char_value = pb.char_value();
+  sample.boolean_value = pb.boolean_value();
+
+  sample.int_value_boxed = pb.int_value_boxed();
+  sample.long_value_boxed = pb.long_value_boxed();
+  sample.float_value_boxed = pb.float_value_boxed();
+  sample.double_value_boxed = pb.double_value_boxed();
+  sample.short_value_boxed = pb.short_value_boxed();
+  sample.char_value_boxed = pb.char_value_boxed();
+  sample.boolean_value_boxed = pb.boolean_value_boxed();
+
+  sample.int_array.assign(pb.int_array().begin(), pb.int_array().end());
+  sample.long_array.assign(pb.long_array().begin(), pb.long_array().end());
+  sample.float_array.assign(pb.float_array().begin(), pb.float_array().end());
+  sample.double_array.assign(pb.double_array().begin(),
+                             pb.double_array().end());
+  sample.short_array.assign(pb.short_array().begin(), pb.short_array().end());
+  sample.char_array.assign(pb.char_array().begin(), pb.char_array().end());
+  sample.boolean_array.assign(pb.boolean_array().begin(),
+                              pb.boolean_array().end());
+  sample.string = pb.string();
   return sample;
 }
 
+protobuf::Sample create_proto_sample() {
+  // Consistent with Java Sample.populate() for fair cross-language comparison
+  return to_pb_sample(create_sample());
+}
+
 MediaContent create_media_content() {
   // Matches Java MediaContent.populate(false) - no circular reference
   MediaContent content;
@@ -410,6 +490,98 @@ protobuf::MediaContent create_proto_media_content() {
   return to_pb_mediaContent(create_media_content());
 }
 
+StructList create_struct_list() {
+  StructList list;
+  list.struct_list.reserve(kListSize);
+  for (int i = 0; i < kListSize; ++i) {
+    list.struct_list.push_back(create_numeric_struct());
+  }
+  return list;
+}
+
+SampleList create_sample_list() {
+  SampleList list;
+  list.sample_list.reserve(kListSize);
+  for (int i = 0; i < kListSize; ++i) {
+    list.sample_list.push_back(create_sample());
+  }
+  return list;
+}
+
+MediaContentList create_media_content_list() {
+  MediaContentList list;
+  list.media_content_list.reserve(kListSize);
+  for (int i = 0; i < kListSize; ++i) {
+    list.media_content_list.push_back(create_media_content());
+  }
+  return list;
+}
+
+inline protobuf::StructList to_pb_struct_list(const StructList &obj) {
+  protobuf::StructList pb;
+  for (const auto &item : obj.struct_list) {
+    *pb.add_struct_list() = to_pb_struct(item);
+  }
+  return pb;
+}
+
+inline StructList from_pb_struct_list(const protobuf::StructList &pb) {
+  StructList list;
+  list.struct_list.reserve(pb.struct_list_size());
+  for (const auto &item : pb.struct_list()) {
+    list.struct_list.push_back(from_pb_struct(item));
+  }
+  return list;
+}
+
+inline protobuf::SampleList to_pb_sample_list(const SampleList &obj) {
+  protobuf::SampleList pb;
+  for (const auto &item : obj.sample_list) {
+    *pb.add_sample_list() = to_pb_sample(item);
+  }
+  return pb;
+}
+
+inline SampleList from_pb_sample_list(const protobuf::SampleList &pb) {
+  SampleList list;
+  list.sample_list.reserve(pb.sample_list_size());
+  for (const auto &item : pb.sample_list()) {
+    list.sample_list.push_back(from_pb_sample(item));
+  }
+  return list;
+}
+
+inline protobuf::MediaContentList
+to_pb_media_content_list(const MediaContentList &obj) {
+  protobuf::MediaContentList pb;
+  for (const auto &item : obj.media_content_list) {
+    *pb.add_media_content_list() = to_pb_mediaContent(item);
+  }
+  return pb;
+}
+
+inline MediaContentList
+from_pb_media_content_list(const protobuf::MediaContentList &pb) {
+  MediaContentList list;
+  list.media_content_list.reserve(pb.media_content_list_size());
+  for (const auto &item : pb.media_content_list()) {
+    list.media_content_list.push_back(from_pb_mediaContent(item));
+  }
+  return list;
+}
+
+protobuf::StructList create_proto_struct_list() {
+  return to_pb_struct_list(create_struct_list());
+}
+
+protobuf::SampleList create_proto_sample_list() {
+  return to_pb_sample_list(create_sample_list());
+}
+
+protobuf::MediaContentList create_proto_media_content_list() {
+  return to_pb_media_content_list(create_media_content_list());
+}
+
 // ============================================================================
 // Helper to configure Fory instance
 // ============================================================================
@@ -420,6 +592,9 @@ void register_fory_types(fory::serialization::Fory &fory) {
   fory.register_struct<Media>(3);
   fory.register_struct<Image>(4);
   fory.register_struct<MediaContent>(5);
+  fory.register_struct<StructList>(6);
+  fory.register_struct<SampleList>(7);
+  fory.register_struct<MediaContentList>(8);
 }
 
 // ============================================================================
@@ -429,8 +604,8 @@ void register_fory_types(fory::serialization::Fory &fory) {
 static void BM_Fory_Struct_Serialize(benchmark::State &state) {
   auto fory = fory::serialization::Fory::builder()
                   .xlang(true)
+                  .compatible(true)
                   .track_ref(false)
-                  .check_struct_version(false)
                   .build();
   register_fory_types(fory);
   NumericStruct obj = create_numeric_struct();
@@ -466,8 +641,8 @@ BENCHMARK(BM_Protobuf_Struct_Serialize);
 static void BM_Fory_Struct_Deserialize(benchmark::State &state) {
   auto fory = fory::serialization::Fory::builder()
                   .xlang(true)
+                  .compatible(true)
                   .track_ref(false)
-                  .check_struct_version(false)
                   .build();
   register_fory_types(fory);
   NumericStruct obj = create_numeric_struct();
@@ -516,8 +691,8 @@ BENCHMARK(BM_Protobuf_Struct_Deserialize);
 static void BM_Fory_Sample_Serialize(benchmark::State &state) {
   auto fory = fory::serialization::Fory::builder()
                   .xlang(true)
+                  .compatible(true)
                   .track_ref(false)
-                  .check_struct_version(false)
                   .build();
   register_fory_types(fory);
   Sample obj = create_sample();
@@ -550,8 +725,8 @@ BENCHMARK(BM_Protobuf_Sample_Serialize);
 static void BM_Fory_Sample_Deserialize(benchmark::State &state) {
   auto fory = fory::serialization::Fory::builder()
                   .xlang(true)
+                  .compatible(true)
                   .track_ref(false)
-                  .check_struct_version(false)
                   .build();
   register_fory_types(fory);
   Sample obj = create_sample();
@@ -596,8 +771,8 @@ BENCHMARK(BM_Protobuf_Sample_Deserialize);
 static void BM_Fory_MediaContent_Serialize(benchmark::State &state) {
   auto fory = fory::serialization::Fory::builder()
                   .xlang(true)
+                  .compatible(true)
                   .track_ref(false)
-                  .check_struct_version(false)
                   .build();
   register_fory_types(fory);
   MediaContent obj = create_media_content();
@@ -632,8 +807,8 @@ BENCHMARK(BM_Protobuf_MediaContent_Serialize);
 static void BM_Fory_MediaContent_Deserialize(benchmark::State &state) {
   auto fory = fory::serialization::Fory::builder()
                   .xlang(true)
+                  .compatible(true)
                   .track_ref(false)
-                  .check_struct_version(false)
                   .build();
   register_fory_types(fory);
   MediaContent obj = create_media_content();
@@ -672,6 +847,243 @@ static void 
BM_Protobuf_MediaContent_Deserialize(benchmark::State &state) {
 }
 BENCHMARK(BM_Protobuf_MediaContent_Deserialize);
 
+// ============================================================================
+// List benchmarks (StructList, SampleList, MediaContentList)
+// ============================================================================
+
+static void BM_Fory_StructList_Serialize(benchmark::State &state) {
+  auto fory = fory::serialization::Fory::builder()
+                  .xlang(true)
+                  .compatible(true)
+                  .track_ref(false)
+                  .build();
+  register_fory_types(fory);
+  StructList obj = create_struct_list();
+
+  fory::Buffer buffer;
+  buffer.reserve(65536);
+
+  for (auto _ : state) {
+    buffer.writer_index(0);
+    auto result = fory.serialize_to(buffer, obj);
+    benchmark::DoNotOptimize(result);
+    benchmark::DoNotOptimize(buffer.data());
+  }
+}
+BENCHMARK(BM_Fory_StructList_Serialize);
+
+static void BM_Protobuf_StructList_Serialize(benchmark::State &state) {
+  StructList obj = create_struct_list();
+  protobuf::StructList pb = to_pb_struct_list(obj);
+  std::vector<uint8_t> output;
+  output.resize(pb.ByteSizeLong());
+
+  for (auto _ : state) {
+    pb = to_pb_struct_list(obj);
+    pb.SerializeToArray(output.data(), static_cast<int>(output.size()));
+    benchmark::DoNotOptimize(output);
+  }
+}
+BENCHMARK(BM_Protobuf_StructList_Serialize);
+
+static void BM_Fory_StructList_Deserialize(benchmark::State &state) {
+  auto fory = fory::serialization::Fory::builder()
+                  .xlang(true)
+                  .compatible(true)
+                  .track_ref(false)
+                  .build();
+  register_fory_types(fory);
+  StructList obj = create_struct_list();
+  auto serialized = fory.serialize(obj);
+  if (!serialized.ok()) {
+    state.SkipWithError("Serialization failed");
+    return;
+  }
+  auto &bytes = serialized.value();
+
+  auto test_result = fory.deserialize<StructList>(bytes.data(), bytes.size());
+  if (!test_result.ok()) {
+    state.SkipWithError("Deserialization test failed");
+    return;
+  }
+
+  for (auto _ : state) {
+    auto result = fory.deserialize<StructList>(bytes.data(), bytes.size());
+    benchmark::DoNotOptimize(result);
+  }
+}
+BENCHMARK(BM_Fory_StructList_Deserialize);
+
+static void BM_Protobuf_StructList_Deserialize(benchmark::State &state) {
+  protobuf::StructList obj = create_proto_struct_list();
+  std::string serialized;
+  obj.SerializeToString(&serialized);
+
+  for (auto _ : state) {
+    protobuf::StructList pb_result;
+    pb_result.ParseFromString(serialized);
+    StructList result = from_pb_struct_list(pb_result);
+    benchmark::DoNotOptimize(result);
+  }
+}
+BENCHMARK(BM_Protobuf_StructList_Deserialize);
+
+static void BM_Fory_SampleList_Serialize(benchmark::State &state) {
+  auto fory = fory::serialization::Fory::builder()
+                  .xlang(true)
+                  .compatible(true)
+                  .track_ref(false)
+                  .build();
+  register_fory_types(fory);
+  SampleList obj = create_sample_list();
+
+  fory::Buffer buffer;
+  buffer.reserve(131072);
+
+  for (auto _ : state) {
+    buffer.writer_index(0);
+    auto result = fory.serialize_to(buffer, obj);
+    benchmark::DoNotOptimize(result);
+    benchmark::DoNotOptimize(buffer.data());
+  }
+}
+BENCHMARK(BM_Fory_SampleList_Serialize);
+
+static void BM_Protobuf_SampleList_Serialize(benchmark::State &state) {
+  SampleList obj = create_sample_list();
+  protobuf::SampleList pb = to_pb_sample_list(obj);
+  std::vector<uint8_t> output;
+  output.resize(pb.ByteSizeLong());
+
+  for (auto _ : state) {
+    pb = to_pb_sample_list(obj);
+    pb.SerializeToArray(output.data(), static_cast<int>(output.size()));
+    benchmark::DoNotOptimize(output);
+  }
+}
+BENCHMARK(BM_Protobuf_SampleList_Serialize);
+
+static void BM_Fory_SampleList_Deserialize(benchmark::State &state) {
+  auto fory = fory::serialization::Fory::builder()
+                  .xlang(true)
+                  .compatible(true)
+                  .track_ref(false)
+                  .build();
+  register_fory_types(fory);
+  SampleList obj = create_sample_list();
+  auto serialized = fory.serialize(obj);
+  if (!serialized.ok()) {
+    state.SkipWithError("Serialization failed");
+    return;
+  }
+  auto &bytes = serialized.value();
+
+  auto test_result = fory.deserialize<SampleList>(bytes.data(), bytes.size());
+  if (!test_result.ok()) {
+    state.SkipWithError("Deserialization test failed");
+    return;
+  }
+
+  for (auto _ : state) {
+    auto result = fory.deserialize<SampleList>(bytes.data(), bytes.size());
+    benchmark::DoNotOptimize(result);
+  }
+}
+BENCHMARK(BM_Fory_SampleList_Deserialize);
+
+static void BM_Protobuf_SampleList_Deserialize(benchmark::State &state) {
+  protobuf::SampleList obj = create_proto_sample_list();
+  std::string serialized;
+  obj.SerializeToString(&serialized);
+
+  for (auto _ : state) {
+    protobuf::SampleList pb_result;
+    pb_result.ParseFromString(serialized);
+    SampleList result = from_pb_sample_list(pb_result);
+    benchmark::DoNotOptimize(result);
+  }
+}
+BENCHMARK(BM_Protobuf_SampleList_Deserialize);
+
+static void BM_Fory_MediaContentList_Serialize(benchmark::State &state) {
+  auto fory = fory::serialization::Fory::builder()
+                  .xlang(true)
+                  .compatible(true)
+                  .track_ref(false)
+                  .build();
+  register_fory_types(fory);
+  MediaContentList obj = create_media_content_list();
+
+  fory::Buffer buffer;
+  buffer.reserve(131072);
+
+  for (auto _ : state) {
+    buffer.writer_index(0);
+    auto result = fory.serialize_to(buffer, obj);
+    benchmark::DoNotOptimize(result);
+    benchmark::DoNotOptimize(buffer.data());
+  }
+}
+BENCHMARK(BM_Fory_MediaContentList_Serialize);
+
+static void BM_Protobuf_MediaContentList_Serialize(benchmark::State &state) {
+  MediaContentList obj = create_media_content_list();
+  protobuf::MediaContentList pb = to_pb_media_content_list(obj);
+  std::vector<uint8_t> output;
+  output.resize(pb.ByteSizeLong());
+
+  for (auto _ : state) {
+    pb = to_pb_media_content_list(obj);
+    pb.SerializeToArray(output.data(), static_cast<int>(output.size()));
+    benchmark::DoNotOptimize(output);
+  }
+}
+BENCHMARK(BM_Protobuf_MediaContentList_Serialize);
+
+static void BM_Fory_MediaContentList_Deserialize(benchmark::State &state) {
+  auto fory = fory::serialization::Fory::builder()
+                  .xlang(true)
+                  .compatible(true)
+                  .track_ref(false)
+                  .build();
+  register_fory_types(fory);
+  MediaContentList obj = create_media_content_list();
+  auto serialized = fory.serialize(obj);
+  if (!serialized.ok()) {
+    state.SkipWithError("Serialization failed");
+    return;
+  }
+  auto &bytes = serialized.value();
+
+  auto test_result =
+      fory.deserialize<MediaContentList>(bytes.data(), bytes.size());
+  if (!test_result.ok()) {
+    state.SkipWithError("Deserialization test failed");
+    return;
+  }
+
+  for (auto _ : state) {
+    auto result =
+        fory.deserialize<MediaContentList>(bytes.data(), bytes.size());
+    benchmark::DoNotOptimize(result);
+  }
+}
+BENCHMARK(BM_Fory_MediaContentList_Deserialize);
+
+static void BM_Protobuf_MediaContentList_Deserialize(benchmark::State &state) {
+  protobuf::MediaContentList obj = create_proto_media_content_list();
+  std::string serialized;
+  obj.SerializeToString(&serialized);
+
+  for (auto _ : state) {
+    protobuf::MediaContentList pb_result;
+    pb_result.ParseFromString(serialized);
+    MediaContentList result = from_pb_media_content_list(pb_result);
+    benchmark::DoNotOptimize(result);
+  }
+}
+BENCHMARK(BM_Protobuf_MediaContentList_Deserialize);
+
 // ============================================================================
 // Serialized size comparison (printed once at the end)
 // ============================================================================
@@ -680,25 +1092,39 @@ static void BM_PrintSerializedSizes(benchmark::State 
&state) {
   // Fory
   auto fory = fory::serialization::Fory::builder()
                   .xlang(true)
+                  .compatible(true)
                   .track_ref(false)
-                  .check_struct_version(false)
                   .build();
   register_fory_types(fory);
   NumericStruct fory_struct = create_numeric_struct();
   Sample fory_sample = create_sample();
   MediaContent fory_media = create_media_content();
+  StructList fory_struct_list = create_struct_list();
+  SampleList fory_sample_list = create_sample_list();
+  MediaContentList fory_media_list = create_media_content_list();
   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();
+  auto fory_struct_list_bytes = fory.serialize(fory_struct_list).value();
+  auto fory_sample_list_bytes = fory.serialize(fory_sample_list).value();
+  auto fory_media_list_bytes = fory.serialize(fory_media_list).value();
 
   // Protobuf
   protobuf::Struct proto_struct = create_proto_struct();
   protobuf::Sample proto_sample = create_proto_sample();
   protobuf::MediaContent proto_media = create_proto_media_content();
-  std::string proto_struct_bytes, proto_sample_bytes, proto_media_bytes;
+  protobuf::StructList proto_struct_list = create_proto_struct_list();
+  protobuf::SampleList proto_sample_list = create_proto_sample_list();
+  protobuf::MediaContentList proto_media_list =
+      create_proto_media_content_list();
+  std::string proto_struct_bytes, proto_sample_bytes, proto_media_bytes,
+      proto_struct_list_bytes, proto_sample_list_bytes, proto_media_list_bytes;
   proto_struct.SerializeToString(&proto_struct_bytes);
   proto_sample.SerializeToString(&proto_sample_bytes);
   proto_media.SerializeToString(&proto_media_bytes);
+  proto_struct_list.SerializeToString(&proto_struct_list_bytes);
+  proto_sample_list.SerializeToString(&proto_sample_list_bytes);
+  proto_media_list.SerializeToString(&proto_media_list_bytes);
 
   for (auto _ : state) {
     // Just run once to print sizes
@@ -710,5 +1136,11 @@ static void BM_PrintSerializedSizes(benchmark::State 
&state) {
   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();
+  state.counters["fory_struct_list_size"] = fory_struct_list_bytes.size();
+  state.counters["proto_struct_list_size"] = proto_struct_list_bytes.size();
+  state.counters["fory_sample_list_size"] = fory_sample_list_bytes.size();
+  state.counters["proto_sample_list_size"] = proto_sample_list_bytes.size();
+  state.counters["fory_media_list_size"] = fory_media_list_bytes.size();
+  state.counters["proto_media_list_size"] = proto_media_list_bytes.size();
 }
 BENCHMARK(BM_PrintSerializedSizes)->Iterations(1);
diff --git a/benchmarks/cpp_benchmark/benchmark_report.py 
b/benchmarks/cpp_benchmark/benchmark_report.py
index 4d9846c3b..e9dbd832f 100644
--- a/benchmarks/cpp_benchmark/benchmark_report.py
+++ b/benchmarks/cpp_benchmark/benchmark_report.py
@@ -97,6 +97,19 @@ def parse_benchmark_name(name):
     return None, None, None
 
 
+def format_datatype_label(datatype):
+    if not datatype:
+        return ""
+    if datatype.endswith("list"):
+        base = datatype[: -len("list")]
+        if base == "mediacontent":
+            return "MediaContent\nList"
+        return f"{base.capitalize()}\nList"
+    if datatype == "mediacontent":
+        return "MediaContent"
+    return datatype.capitalize()
+
+
 # === Read and parse benchmark JSON ===
 def load_benchmark_data(json_file):
     with open(json_file, "r", encoding="utf-8") as f:
@@ -127,6 +140,14 @@ for bench in benchmark_data.get("benchmarks", []):
                 "proto_struct_size",
                 "fory_sample_size",
                 "proto_sample_size",
+                "fory_media_size",
+                "proto_media_size",
+                "fory_struct_list_size",
+                "proto_struct_list_size",
+                "fory_sample_list_size",
+                "proto_sample_list_size",
+                "fory_media_list_size",
+                "proto_media_list_size",
             ]:
                 if key in bench:
                     sizes[key] = int(bench[key])
@@ -235,7 +256,7 @@ for idx, op in enumerate(operations):
     ax.set_ylabel("Throughput (ops/sec)")
     ax.set_title(f"{op.capitalize()} Throughput (higher is better)")
     ax.set_xticks(x)
-    ax.set_xticklabels([dt.capitalize() for dt in datatypes])
+    ax.set_xticklabels([format_datatype_label(dt) for dt in datatypes])
     ax.legend()
     ax.grid(True, axis="y", linestyle="--", alpha=0.5)
 
@@ -330,6 +351,22 @@ if sizes:
         md_report.append(
             f"| Sample | {sizes['fory_sample_size']} | 
{sizes['proto_sample_size']} |\n"
         )
+    if "fory_media_size" in sizes and "proto_media_size" in sizes:
+        md_report.append(
+            f"| MediaContent | {sizes['fory_media_size']} | 
{sizes['proto_media_size']} |\n"
+        )
+    if "fory_struct_list_size" in sizes and "proto_struct_list_size" in sizes:
+        md_report.append(
+            f"| StructList | {sizes['fory_struct_list_size']} | 
{sizes['proto_struct_list_size']} |\n"
+        )
+    if "fory_sample_list_size" in sizes and "proto_sample_list_size" in sizes:
+        md_report.append(
+            f"| SampleList | {sizes['fory_sample_list_size']} | 
{sizes['proto_sample_list_size']} |\n"
+        )
+    if "fory_media_list_size" in sizes and "proto_media_list_size" in sizes:
+        md_report.append(
+            f"| MediaContentList | {sizes['fory_media_list_size']} | 
{sizes['proto_media_list_size']} |\n"
+        )
 
 # Save Markdown
 report_path = os.path.join(output_dir, "REPORT.md")
diff --git a/benchmarks/go_benchmark/README.md 
b/benchmarks/go_benchmark/README.md
index fbc467b4d..8c008711e 100644
--- a/benchmarks/go_benchmark/README.md
+++ b/benchmarks/go_benchmark/README.md
@@ -10,11 +10,14 @@ This directory contains benchmarks comparing [Apache 
Fory](https://github.com/ap
 
 ## Data Types Benchmarked
 
-| Data Type     | Description                                      |
-| ------------- | ------------------------------------------------ |
-| NumericStruct | Simple struct with 8 int32 fields                |
-| Sample        | Complex struct with primitives and 7 array types |
-| MediaContent  | Nested objects with strings, enums, and lists    |
+| Data Type        | Description                                      |
+| ---------------- | ------------------------------------------------ |
+| NumericStruct    | Simple struct with 8 int32 fields                |
+| Sample           | Complex struct with primitives and 7 array types |
+| MediaContent     | Nested objects with strings, enums, and lists    |
+| StructList       | List of NumericStruct (20 elements)              |
+| SampleList       | List of Sample (20 elements)                     |
+| MediaContentList | List of MediaContent (20 elements)               |
 
 The benchmark data matches the C++ benchmark for cross-language comparison.
 
@@ -47,6 +50,9 @@ apt-get install protobuf-compiler
 ./run.sh --data struct
 ./run.sh --data sample
 ./run.sh --data mediacontent
+./run.sh --data structlist
+./run.sh --data samplelist
+./run.sh --data mediacontentlist
 
 # Run specific serializer
 ./run.sh --serializer fory
@@ -96,7 +102,7 @@ Example results on Apple M1 Pro:
 | Mediacontent | Serialize   | 4.93M        | 2.30M            | 1.17M         
  | 2.14x      | 4.21x      |
 | Mediacontent | Deserialize | 2.74M        | 1.87M            | 751K          
  | 1.46x      | 3.65x      |
 
-_Note: Results vary by hardware. Run benchmarks on your own system for 
accurate comparisons._
+_Note: Results vary by hardware. Run benchmarks on your own system for 
accurate comparisons. List benchmarks are included in the generated report._
 
 ## Benchmark Methodology
 
diff --git a/benchmarks/go_benchmark/benchmark_report.py 
b/benchmarks/go_benchmark/benchmark_report.py
index 2fd76fa01..d00418a2b 100755
--- a/benchmarks/go_benchmark/benchmark_report.py
+++ b/benchmarks/go_benchmark/benchmark_report.py
@@ -45,6 +45,16 @@ COLORS = {
     "protobuf": "#55BCC2",  # Teal
     "msgpack": "#9B59B6",  # Purple
 }
+DATATYPES = [
+    "struct",
+    "structlist",
+    "sample",
+    "samplelist",
+    "mediacontent",
+    "mediacontentlist",
+]
+OPERATIONS = ["serialize", "deserialize"]
+SERIALIZERS = ["fory", "protobuf", "msgpack"]
 
 
 def parse_benchmark_txt(filepath):
@@ -107,16 +117,49 @@ def parse_benchmark_json(filepath):
     return final_results
 
 
+def parse_serialized_sizes(text):
+    sizes = {}
+    current = None
+    for line in text.splitlines():
+        line = line.strip()
+        if not line or line.startswith("="):
+            continue
+        if line.endswith(":") and not line.startswith(
+            ("Fory:", "Protobuf:", "Msgpack:")
+        ):
+            current = line.rstrip(":")
+            sizes[current] = {}
+            continue
+        if current is None:
+            continue
+        match = re.match(r"^(Fory|Protobuf|Msgpack):\s+(\d+)\s+bytes$", line)
+        if match:
+            serializer = match.group(1).lower()
+            size = int(match.group(2))
+            sizes[current][serializer] = size
+    return sizes
+
+
+def load_serialized_sizes(output_dir):
+    size_files = [
+        Path(output_dir) / "serialized_sizes.txt",
+        Path(output_dir) / "benchmark_results.txt",
+    ]
+    for path in size_files:
+        if not path.exists():
+            continue
+        text = path.read_text(encoding="utf-8", errors="ignore")
+        if "Serialized Sizes (bytes):" in text:
+            return parse_serialized_sizes(text)
+    return {}
+
+
 def generate_plots(results, output_dir):
     """Generate comparison plots for each data type."""
     if not HAS_MATPLOTLIB:
         return
 
-    datatypes = ["struct", "sample", "mediacontent"]
-    operations = ["serialize", "deserialize"]
-    serializers = ["fory", "protobuf", "msgpack"]
-
-    for datatype in datatypes:
+    for datatype in DATATYPES:
         if datatype not in results:
             continue
 
@@ -127,14 +170,14 @@ def generate_plots(results, output_dir):
             fontweight="bold",
         )
 
-        for idx, op in enumerate(operations):
+        for idx, op in enumerate(OPERATIONS):
             ax = axes[idx]
 
             if op not in results[datatype]:
                 continue
 
             data = results[datatype][op]
-            available_serializers = [s for s in serializers if s in data]
+            available_serializers = [s for s in SERIALIZERS if s in data]
 
             if not available_serializers:
                 continue
@@ -195,11 +238,15 @@ def generate_combined_plot(results, output_dir):
     if not HAS_MATPLOTLIB:
         return
 
-    datatypes = ["struct", "sample", "mediacontent"]
-    operations = ["serialize", "deserialize"]
-    serializers = ["fory", "protobuf", "msgpack"]
+    datatypes = DATATYPES
+    operations = OPERATIONS
+    serializers = SERIALIZERS
 
-    fig, axes = plt.subplots(2, 3, figsize=(15, 10))
+    cols = len(datatypes)
+    fig_width = max(12, 3.5 * cols)
+    fig, axes = plt.subplots(len(operations), cols, figsize=(fig_width, 10))
+    if cols == 1:
+        axes = [[axes[row]] for row in range(len(operations))]
     fig.suptitle(
         "Go Serialization Benchmark: Fory vs Protobuf vs Msgpack",
         fontsize=14,
@@ -208,7 +255,7 @@ def generate_combined_plot(results, output_dir):
 
     for row, op in enumerate(operations):
         for col, datatype in enumerate(datatypes):
-            ax = axes[row, col]
+            ax = axes[row][col]
 
             if datatype not in results or op not in results[datatype]:
                 ax.text(
@@ -259,8 +306,8 @@ def generate_combined_plot(results, output_dir):
 
 def generate_markdown_report(results, output_dir):
     """Generate markdown report."""
-    datatypes = ["struct", "sample", "mediacontent"]
-    operations = ["serialize", "deserialize"]
+    datatypes = DATATYPES
+    operations = OPERATIONS
 
     report = []
     report.append("# Go Serialization Benchmark Report\n")
@@ -336,6 +383,38 @@ def generate_markdown_report(results, output_dir):
 
     report.append("")
 
+    # Serialized size section
+    sizes = load_serialized_sizes(output_dir)
+    report.append("### Serialized Data Sizes (bytes)\n")
+    if sizes:
+        report.append("| Data Type | Fory | Protobuf | Msgpack |")
+        report.append("|-----------|------|----------|---------|")
+        name_map = {
+            "NumericStruct": "Struct",
+            "Sample": "Sample",
+            "MediaContent": "MediaContent",
+            "StructList": "StructList",
+            "SampleList": "SampleList",
+            "MediaContentList": "MediaContentList",
+        }
+        ordered = [
+            "NumericStruct",
+            "Sample",
+            "MediaContent",
+            "StructList",
+            "SampleList",
+            "MediaContentList",
+        ]
+        for key in ordered:
+            if key not in sizes:
+                continue
+            entry = sizes[key]
+            report.append(
+                f"| {name_map.get(key, key)} | {entry.get('fory', 'N/A')} | 
{entry.get('protobuf', 'N/A')} | {entry.get('msgpack', 'N/A')} |"
+            )
+    else:
+        report.append("No serialized size data found.\n")
+
     # Plots section
     if HAS_MATPLOTLIB:
         report.append("## Performance Charts\n")
diff --git a/benchmarks/go_benchmark/benchmark_test.go 
b/benchmarks/go_benchmark/benchmark_test.go
index 2366d0a11..e0e1039c6 100644
--- a/benchmarks/go_benchmark/benchmark_test.go
+++ b/benchmarks/go_benchmark/benchmark_test.go
@@ -35,6 +35,7 @@ func newFory() *fory.Fory {
        f := fory.New(
                fory.WithXlang(true),
                fory.WithTrackRef(false),
+               fory.WithCompatible(true),
        )
        // Register types with IDs matching C++ benchmark
        if err := f.RegisterStruct(NumericStruct{}, 1); err != nil {
@@ -52,6 +53,15 @@ func newFory() *fory.Fory {
        if err := f.RegisterStruct(MediaContent{}, 5); err != nil {
                panic(err)
        }
+       if err := f.RegisterStruct(StructList{}, 8); err != nil {
+               panic(err)
+       }
+       if err := f.RegisterStruct(SampleList{}, 9); err != nil {
+               panic(err)
+       }
+       if err := f.RegisterStruct(MediaContentList{}, 10); err != nil {
+               panic(err)
+       }
        if err := f.RegisterEnum(Player(0), 6); err != nil {
                panic(err)
        }
@@ -162,6 +172,104 @@ func BenchmarkMsgpack_Struct_Deserialize(b *testing.B) {
        }
 }
 
+// ============================================================================
+// StructList Benchmarks
+// ============================================================================
+
+func BenchmarkFory_StructList_Serialize(b *testing.B) {
+       f := newFory()
+       obj := CreateStructList()
+       buf := fory.NewByteBuffer(make([]byte, 0, 65536))
+
+       b.ResetTimer()
+       for i := 0; i < b.N; i++ {
+               buf.Reset()
+               err := f.SerializeTo(buf, &obj)
+               if err != nil {
+                       b.Fatal(err)
+               }
+       }
+}
+
+func BenchmarkProtobuf_StructList_Serialize(b *testing.B) {
+       obj := CreateStructList()
+
+       b.ResetTimer()
+       for i := 0; i < b.N; i++ {
+               pbObj := ToPbStructList(obj)
+               _, err := proto.Marshal(pbObj)
+               if err != nil {
+                       b.Fatal(err)
+               }
+       }
+}
+
+func BenchmarkMsgpack_StructList_Serialize(b *testing.B) {
+       obj := CreateStructList()
+
+       b.ResetTimer()
+       for i := 0; i < b.N; i++ {
+               _, err := msgpack.Marshal(obj)
+               if err != nil {
+                       b.Fatal(err)
+               }
+       }
+}
+
+func BenchmarkFory_StructList_Deserialize(b *testing.B) {
+       f := newFory()
+       obj := CreateStructList()
+       data, err := f.Serialize(&obj)
+       if err != nil {
+               b.Fatal(err)
+       }
+
+       b.ResetTimer()
+       for i := 0; i < b.N; i++ {
+               var result StructList
+               err := f.Deserialize(data, &result)
+               if err != nil {
+                       b.Fatal(err)
+               }
+       }
+}
+
+func BenchmarkProtobuf_StructList_Deserialize(b *testing.B) {
+       obj := CreateStructList()
+       pbObj := ToPbStructList(obj)
+       data, err := proto.Marshal(pbObj)
+       if err != nil {
+               b.Fatal(err)
+       }
+
+       b.ResetTimer()
+       for i := 0; i < b.N; i++ {
+               var pbResult pb.StructList
+               err := proto.Unmarshal(data, &pbResult)
+               if err != nil {
+                       b.Fatal(err)
+               }
+               _ = FromPbStructList(&pbResult)
+       }
+}
+
+func BenchmarkMsgpack_StructList_Deserialize(b *testing.B) {
+       obj := CreateStructList()
+       data, err := msgpack.Marshal(obj)
+       if err != nil {
+               b.Fatal(err)
+       }
+
+       b.ResetTimer()
+       for i := 0; i < b.N; i++ {
+               var result StructList
+               err := msgpack.Unmarshal(data, &result)
+               if err != nil {
+                       b.Fatal(err)
+               }
+       }
+}
+
 // ============================================================================
 // Sample Benchmarks
 // ============================================================================
@@ -260,6 +368,104 @@ func BenchmarkMsgpack_Sample_Deserialize(b *testing.B) {
        }
 }
 
+// ============================================================================
+// SampleList Benchmarks
+// ============================================================================
+
+func BenchmarkFory_SampleList_Serialize(b *testing.B) {
+       f := newFory()
+       obj := CreateSampleList()
+       buf := fory.NewByteBuffer(make([]byte, 0, 131072))
+
+       b.ResetTimer()
+       for i := 0; i < b.N; i++ {
+               buf.Reset()
+               err := f.SerializeTo(buf, &obj)
+               if err != nil {
+                       b.Fatal(err)
+               }
+       }
+}
+
+func BenchmarkProtobuf_SampleList_Serialize(b *testing.B) {
+       obj := CreateSampleList()
+
+       b.ResetTimer()
+       for i := 0; i < b.N; i++ {
+               pbObj := ToPbSampleList(obj)
+               _, err := proto.Marshal(pbObj)
+               if err != nil {
+                       b.Fatal(err)
+               }
+       }
+}
+
+func BenchmarkMsgpack_SampleList_Serialize(b *testing.B) {
+       obj := CreateSampleList()
+
+       b.ResetTimer()
+       for i := 0; i < b.N; i++ {
+               _, err := msgpack.Marshal(obj)
+               if err != nil {
+                       b.Fatal(err)
+               }
+       }
+}
+
+func BenchmarkFory_SampleList_Deserialize(b *testing.B) {
+       f := newFory()
+       obj := CreateSampleList()
+       data, err := f.Serialize(&obj)
+       if err != nil {
+               b.Fatal(err)
+       }
+
+       b.ResetTimer()
+       for i := 0; i < b.N; i++ {
+               var result SampleList
+               err := f.Deserialize(data, &result)
+               if err != nil {
+                       b.Fatal(err)
+               }
+       }
+}
+
+func BenchmarkProtobuf_SampleList_Deserialize(b *testing.B) {
+       obj := CreateSampleList()
+       pbObj := ToPbSampleList(obj)
+       data, err := proto.Marshal(pbObj)
+       if err != nil {
+               b.Fatal(err)
+       }
+
+       b.ResetTimer()
+       for i := 0; i < b.N; i++ {
+               var pbResult pb.SampleList
+               err := proto.Unmarshal(data, &pbResult)
+               if err != nil {
+                       b.Fatal(err)
+               }
+               _ = FromPbSampleList(&pbResult)
+       }
+}
+
+func BenchmarkMsgpack_SampleList_Deserialize(b *testing.B) {
+       obj := CreateSampleList()
+       data, err := msgpack.Marshal(obj)
+       if err != nil {
+               b.Fatal(err)
+       }
+
+       b.ResetTimer()
+       for i := 0; i < b.N; i++ {
+               var result SampleList
+               err := msgpack.Unmarshal(data, &result)
+               if err != nil {
+                       b.Fatal(err)
+               }
+       }
+}
+
 // ============================================================================
 // MediaContent Benchmarks
 // ============================================================================
@@ -358,6 +564,104 @@ func BenchmarkMsgpack_MediaContent_Deserialize(b 
*testing.B) {
        }
 }
 
+// ============================================================================
+// MediaContentList Benchmarks
+// ============================================================================
+
+func BenchmarkFory_MediaContentList_Serialize(b *testing.B) {
+       f := newFory()
+       obj := CreateMediaContentList()
+       buf := fory.NewByteBuffer(make([]byte, 0, 131072))
+
+       b.ResetTimer()
+       for i := 0; i < b.N; i++ {
+               buf.Reset()
+               err := f.SerializeTo(buf, &obj)
+               if err != nil {
+                       b.Fatal(err)
+               }
+       }
+}
+
+func BenchmarkProtobuf_MediaContentList_Serialize(b *testing.B) {
+       obj := CreateMediaContentList()
+
+       b.ResetTimer()
+       for i := 0; i < b.N; i++ {
+               pbObj := ToPbMediaContentList(obj)
+               _, err := proto.Marshal(pbObj)
+               if err != nil {
+                       b.Fatal(err)
+               }
+       }
+}
+
+func BenchmarkMsgpack_MediaContentList_Serialize(b *testing.B) {
+       obj := CreateMediaContentList()
+
+       b.ResetTimer()
+       for i := 0; i < b.N; i++ {
+               _, err := msgpack.Marshal(obj)
+               if err != nil {
+                       b.Fatal(err)
+               }
+       }
+}
+
+func BenchmarkFory_MediaContentList_Deserialize(b *testing.B) {
+       f := newFory()
+       obj := CreateMediaContentList()
+       data, err := f.Serialize(&obj)
+       if err != nil {
+               b.Fatal(err)
+       }
+
+       b.ResetTimer()
+       for i := 0; i < b.N; i++ {
+               var result MediaContentList
+               err := f.Deserialize(data, &result)
+               if err != nil {
+                       b.Fatal(err)
+               }
+       }
+}
+
+func BenchmarkProtobuf_MediaContentList_Deserialize(b *testing.B) {
+       obj := CreateMediaContentList()
+       pbObj := ToPbMediaContentList(obj)
+       data, err := proto.Marshal(pbObj)
+       if err != nil {
+               b.Fatal(err)
+       }
+
+       b.ResetTimer()
+       for i := 0; i < b.N; i++ {
+               var pbResult pb.MediaContentList
+               err := proto.Unmarshal(data, &pbResult)
+               if err != nil {
+                       b.Fatal(err)
+               }
+               _ = FromPbMediaContentList(&pbResult)
+       }
+}
+
+func BenchmarkMsgpack_MediaContentList_Deserialize(b *testing.B) {
+       obj := CreateMediaContentList()
+       data, err := msgpack.Marshal(obj)
+       if err != nil {
+               b.Fatal(err)
+       }
+
+       b.ResetTimer()
+       for i := 0; i < b.N; i++ {
+               var result MediaContentList
+               err := msgpack.Unmarshal(data, &result)
+               if err != nil {
+                       b.Fatal(err)
+               }
+       }
+}
+
 // ============================================================================
 // Size Comparison (run once to print sizes)
 // ============================================================================
@@ -383,6 +687,24 @@ func TestPrintSerializedSizes(t *testing.T) {
        pbMediaData, _ := proto.Marshal(ToPbMediaContent(mediaContent))
        msgpackMediaData, _ := msgpack.Marshal(mediaContent)
 
+       // StructList sizes
+       structList := CreateStructList()
+       foryStructListData, _ := f.Serialize(&structList)
+       pbStructListData, _ := proto.Marshal(ToPbStructList(structList))
+       msgpackStructListData, _ := msgpack.Marshal(structList)
+
+       // SampleList sizes
+       sampleList := CreateSampleList()
+       forySampleListData, _ := f.Serialize(&sampleList)
+       pbSampleListData, _ := proto.Marshal(ToPbSampleList(sampleList))
+       msgpackSampleListData, _ := msgpack.Marshal(sampleList)
+
+       // MediaContentList sizes
+       mediaContentList := CreateMediaContentList()
+       foryMediaContentListData, _ := f.Serialize(&mediaContentList)
+       pbMediaContentListData, _ := 
proto.Marshal(ToPbMediaContentList(mediaContentList))
+       msgpackMediaContentListData, _ := msgpack.Marshal(mediaContentList)
+
        fmt.Println("============================================")
        fmt.Println("Serialized Sizes (bytes):")
        fmt.Println("============================================")
@@ -398,5 +720,17 @@ func TestPrintSerializedSizes(t *testing.T) {
        fmt.Printf("  Fory:     %d bytes\n", len(foryMediaData))
        fmt.Printf("  Protobuf: %d bytes\n", len(pbMediaData))
        fmt.Printf("  Msgpack:  %d bytes\n", len(msgpackMediaData))
+       fmt.Printf("StructList:\n")
+       fmt.Printf("  Fory:     %d bytes\n", len(foryStructListData))
+       fmt.Printf("  Protobuf: %d bytes\n", len(pbStructListData))
+       fmt.Printf("  Msgpack:  %d bytes\n", len(msgpackStructListData))
+       fmt.Printf("SampleList:\n")
+       fmt.Printf("  Fory:     %d bytes\n", len(forySampleListData))
+       fmt.Printf("  Protobuf: %d bytes\n", len(pbSampleListData))
+       fmt.Printf("  Msgpack:  %d bytes\n", len(msgpackSampleListData))
+       fmt.Printf("MediaContentList:\n")
+       fmt.Printf("  Fory:     %d bytes\n", len(foryMediaContentListData))
+       fmt.Printf("  Protobuf: %d bytes\n", len(pbMediaContentListData))
+       fmt.Printf("  Msgpack:  %d bytes\n", len(msgpackMediaContentListData))
        fmt.Println("============================================")
 }
diff --git a/benchmarks/go_benchmark/models.go 
b/benchmarks/go_benchmark/models.go
index 5556182e8..c80107b6b 100644
--- a/benchmarks/go_benchmark/models.go
+++ b/benchmarks/go_benchmark/models.go
@@ -20,41 +20,41 @@ package benchmark
 // NumericStruct is a simple struct with 8 int32 fields
 // Matches the C++ NumericStruct and protobuf Struct message
 type NumericStruct struct {
-       F1 int32 `msgpack:"f1"`
-       F2 int32 `msgpack:"f2"`
-       F3 int32 `msgpack:"f3"`
-       F4 int32 `msgpack:"f4"`
-       F5 int32 `msgpack:"f5"`
-       F6 int32 `msgpack:"f6"`
-       F7 int32 `msgpack:"f7"`
-       F8 int32 `msgpack:"f8"`
+       F1 int32 `msgpack:"1" fory:"id=1"`
+       F2 int32 `msgpack:"2" fory:"id=2"`
+       F3 int32 `msgpack:"3" fory:"id=3"`
+       F4 int32 `msgpack:"4" fory:"id=4"`
+       F5 int32 `msgpack:"5" fory:"id=5"`
+       F6 int32 `msgpack:"6" fory:"id=6"`
+       F7 int32 `msgpack:"7" fory:"id=7"`
+       F8 int32 `msgpack:"8" fory:"id=8"`
 }
 
 // Sample is a complex struct with various types and arrays
 // Matches the C++ Sample and protobuf Sample message
 type Sample struct {
-       IntValue          int32     `msgpack:"int_value"`
-       LongValue         int64     `msgpack:"long_value"`
-       FloatValue        float32   `msgpack:"float_value"`
-       DoubleValue       float64   `msgpack:"double_value"`
-       ShortValue        int32     `msgpack:"short_value"`
-       CharValue         int32     `msgpack:"char_value"`
-       BooleanValue      bool      `msgpack:"boolean_value"`
-       IntValueBoxed     int32     `msgpack:"int_value_boxed"`
-       LongValueBoxed    int64     `msgpack:"long_value_boxed"`
-       FloatValueBoxed   float32   `msgpack:"float_value_boxed"`
-       DoubleValueBoxed  float64   `msgpack:"double_value_boxed"`
-       ShortValueBoxed   int32     `msgpack:"short_value_boxed"`
-       CharValueBoxed    int32     `msgpack:"char_value_boxed"`
-       BooleanValueBoxed bool      `msgpack:"boolean_value_boxed"`
-       IntArray          []int32   `msgpack:"int_array"`
-       LongArray         []int64   `msgpack:"long_array"`
-       FloatArray        []float32 `msgpack:"float_array"`
-       DoubleArray       []float64 `msgpack:"double_array"`
-       ShortArray        []int32   `msgpack:"short_array"`
-       CharArray         []int32   `msgpack:"char_array"`
-       BooleanArray      []bool    `msgpack:"boolean_array"`
-       String            string    `msgpack:"string"`
+       IntValue          int32     `msgpack:"1" fory:"id=1"`
+       LongValue         int64     `msgpack:"2" fory:"id=2"`
+       FloatValue        float32   `msgpack:"3" fory:"id=3"`
+       DoubleValue       float64   `msgpack:"4" fory:"id=4"`
+       ShortValue        int32     `msgpack:"5" fory:"id=5"`
+       CharValue         int32     `msgpack:"6" fory:"id=6"`
+       BooleanValue      bool      `msgpack:"7" fory:"id=7"`
+       IntValueBoxed     int32     `msgpack:"8" fory:"id=8"`
+       LongValueBoxed    int64     `msgpack:"9" fory:"id=9"`
+       FloatValueBoxed   float32   `msgpack:"10" fory:"id=10"`
+       DoubleValueBoxed  float64   `msgpack:"11" fory:"id=11"`
+       ShortValueBoxed   int32     `msgpack:"12" fory:"id=12"`
+       CharValueBoxed    int32     `msgpack:"13" fory:"id=13"`
+       BooleanValueBoxed bool      `msgpack:"14" fory:"id=14"`
+       IntArray          []int32   `msgpack:"15" fory:"id=15"`
+       LongArray         []int64   `msgpack:"16" fory:"id=16"`
+       FloatArray        []float32 `msgpack:"17" fory:"id=17"`
+       DoubleArray       []float64 `msgpack:"18" fory:"id=18"`
+       ShortArray        []int32   `msgpack:"19" fory:"id=19"`
+       CharArray         []int32   `msgpack:"20" fory:"id=20"`
+       BooleanArray      []bool    `msgpack:"21" fory:"id=21"`
+       String            string    `msgpack:"22" fory:"id=22"`
 }
 
 // Player enum type
@@ -75,33 +75,45 @@ const (
 
 // Media represents media metadata
 type Media struct {
-       URI        string   `msgpack:"uri"`
-       Title      string   `msgpack:"title"`
-       Width      int32    `msgpack:"width"`
-       Height     int32    `msgpack:"height"`
-       Format     string   `msgpack:"format"`
-       Duration   int64    `msgpack:"duration"`
-       Size       int64    `msgpack:"size"`
-       Bitrate    int32    `msgpack:"bitrate"`
-       HasBitrate bool     `msgpack:"has_bitrate"`
-       Persons    []string `msgpack:"persons"`
-       Player     Player   `msgpack:"player"`
-       Copyright  string   `msgpack:"copyright"`
+       URI        string   `msgpack:"1" fory:"id=1"`
+       Title      string   `msgpack:"2" fory:"id=2"`
+       Width      int32    `msgpack:"3" fory:"id=3"`
+       Height     int32    `msgpack:"4" fory:"id=4"`
+       Format     string   `msgpack:"5" fory:"id=5"`
+       Duration   int64    `msgpack:"6" fory:"id=6"`
+       Size       int64    `msgpack:"7" fory:"id=7"`
+       Bitrate    int32    `msgpack:"8" fory:"id=8"`
+       HasBitrate bool     `msgpack:"9" fory:"id=9"`
+       Persons    []string `msgpack:"10" fory:"id=10"`
+       Player     Player   `msgpack:"11" fory:"id=11"`
+       Copyright  string   `msgpack:"12" fory:"id=12"`
 }
 
 // Image represents image metadata
 type Image struct {
-       URI    string `msgpack:"uri"`
-       Title  string `msgpack:"title"`
-       Width  int32  `msgpack:"width"`
-       Height int32  `msgpack:"height"`
-       Size   Size   `msgpack:"size"`
+       URI    string `msgpack:"1" fory:"id=1"`
+       Title  string `msgpack:"2" fory:"id=2"`
+       Width  int32  `msgpack:"3" fory:"id=3"`
+       Height int32  `msgpack:"4" fory:"id=4"`
+       Size   Size   `msgpack:"5" fory:"id=5"`
 }
 
 // MediaContent contains media and images
 type MediaContent struct {
-       Media  Media   `msgpack:"media"`
-       Images []Image `msgpack:"images"`
+       Media  Media   `msgpack:"1" fory:"id=1"`
+       Images []Image `msgpack:"2" fory:"id=2"`
+}
+
+type StructList struct {
+       StructList []NumericStruct `msgpack:"1" fory:"id=1"`
+}
+
+type SampleList struct {
+       SampleList []Sample `msgpack:"1" fory:"id=1"`
+}
+
+type MediaContentList struct {
+       MediaContentList []MediaContent `msgpack:"1" fory:"id=1"`
 }
 
 // CreateNumericStruct creates test data matching C++ benchmark
@@ -188,3 +200,27 @@ func CreateMediaContent() MediaContent {
                },
        }
 }
+
+func CreateStructList() StructList {
+       list := make([]NumericStruct, 20)
+       for i := range list {
+               list[i] = CreateNumericStruct()
+       }
+       return StructList{StructList: list}
+}
+
+func CreateSampleList() SampleList {
+       list := make([]Sample, 20)
+       for i := range list {
+               list[i] = CreateSample()
+       }
+       return SampleList{SampleList: list}
+}
+
+func CreateMediaContentList() MediaContentList {
+       list := make([]MediaContent, 20)
+       for i := range list {
+               list[i] = CreateMediaContent()
+       }
+       return MediaContentList{MediaContentList: list}
+}
diff --git a/benchmarks/go_benchmark/proto_convert.go 
b/benchmarks/go_benchmark/proto_convert.go
index d1e4d50b4..fd2a6841b 100644
--- a/benchmarks/go_benchmark/proto_convert.go
+++ b/benchmarks/go_benchmark/proto_convert.go
@@ -49,6 +49,27 @@ func FromPbStruct(pb *pb.Struct) NumericStruct {
        }
 }
 
+// ToPbStructList converts StructList to protobuf StructList
+func ToPbStructList(obj StructList) *pb.StructList {
+       list := make([]*pb.Struct, len(obj.StructList))
+       for i, item := range obj.StructList {
+               list[i] = ToPbStruct(item)
+       }
+       return &pb.StructList{StructList: list}
+}
+
+// FromPbStructList converts protobuf StructList to StructList
+func FromPbStructList(pbList *pb.StructList) StructList {
+       if pbList == nil {
+               return StructList{}
+       }
+       list := make([]NumericStruct, len(pbList.StructList))
+       for i, item := range pbList.StructList {
+               list[i] = FromPbStruct(item)
+       }
+       return StructList{StructList: list}
+}
+
 // ToPbSample converts Sample to protobuf Sample
 func ToPbSample(obj Sample) *pb.Sample {
        return &pb.Sample{
@@ -105,6 +126,27 @@ func FromPbSample(pb *pb.Sample) Sample {
        }
 }
 
+// ToPbSampleList converts SampleList to protobuf SampleList
+func ToPbSampleList(obj SampleList) *pb.SampleList {
+       list := make([]*pb.Sample, len(obj.SampleList))
+       for i, item := range obj.SampleList {
+               list[i] = ToPbSample(item)
+       }
+       return &pb.SampleList{SampleList: list}
+}
+
+// FromPbSampleList converts protobuf SampleList to SampleList
+func FromPbSampleList(pbList *pb.SampleList) SampleList {
+       if pbList == nil {
+               return SampleList{}
+       }
+       list := make([]Sample, len(pbList.SampleList))
+       for i, item := range pbList.SampleList {
+               list[i] = FromPbSample(item)
+       }
+       return SampleList{SampleList: list}
+}
+
 // ToPbImage converts Image to protobuf Image
 func ToPbImage(obj Image) *pb.Image {
        pbImg := &pb.Image{
@@ -200,3 +242,24 @@ func FromPbMediaContent(pbMC *pb.MediaContent) 
MediaContent {
                Images: images,
        }
 }
+
+// ToPbMediaContentList converts MediaContentList to protobuf MediaContentList
+func ToPbMediaContentList(obj MediaContentList) *pb.MediaContentList {
+       list := make([]*pb.MediaContent, len(obj.MediaContentList))
+       for i, item := range obj.MediaContentList {
+               list[i] = ToPbMediaContent(item)
+       }
+       return &pb.MediaContentList{MediaContentList: list}
+}
+
+// FromPbMediaContentList converts protobuf MediaContentList to 
MediaContentList
+func FromPbMediaContentList(pbList *pb.MediaContentList) MediaContentList {
+       if pbList == nil {
+               return MediaContentList{}
+       }
+       list := make([]MediaContent, len(pbList.MediaContentList))
+       for i, item := range pbList.MediaContentList {
+               list[i] = FromPbMediaContent(item)
+       }
+       return MediaContentList{MediaContentList: list}
+}
diff --git a/benchmarks/go_benchmark/run.sh b/benchmarks/go_benchmark/run.sh
index e0fb0216d..448da66a5 100755
--- a/benchmarks/go_benchmark/run.sh
+++ b/benchmarks/go_benchmark/run.sh
@@ -59,7 +59,7 @@ while [[ $# -gt 0 ]]; do
             echo "Usage: $0 [options]"
             echo ""
             echo "Options:"
-            echo "  --data <type>       Filter by data type: struct, sample, 
mediacontent"
+            echo "  --data <type>       Filter by data type: struct, sample, 
mediacontent, structlist, samplelist, mediacontentlist"
             echo "  --serializer <name> Filter by serializer: fory, protobuf, 
msgpack"
             echo "  --count <n>         Number of benchmark runs (default: 5)"
             echo "  --benchtime <dur>   Time for each benchmark (default: 1s)"
@@ -87,12 +87,21 @@ if [[ -n "$DATA_TYPE" ]]; then
         struct)
             FILTER="Struct"
             ;;
+        structlist|struct-list)
+            FILTER="StructList"
+            ;;
         sample)
             FILTER="Sample"
             ;;
+        samplelist|sample-list)
+            FILTER="SampleList"
+            ;;
         mediacontent|media)
             FILTER="MediaContent"
             ;;
+        mediacontentlist|medialist|media-list)
+            FILTER="MediaContentList"
+            ;;
         *)
             echo "Unknown data type: $DATA_TYPE"
             exit 1
@@ -189,7 +198,9 @@ go test $BENCH_ARGS -benchmem -count=1 
-benchtime=$BENCHTIME -json > "$OUTPUT_DI
 
 # Print serialized sizes
 echo ""
-go test -run TestPrintSerializedSizes -v 2>&1 | grep -A 20 "Serialized Sizes"
+go test -run TestPrintSerializedSizes -v 2>&1 | \
+    grep -A 20 "Serialized Sizes" | \
+    tee "$OUTPUT_DIR/serialized_sizes.txt"
 
 # Generate report
 if $GENERATE_REPORT; then
diff --git a/benchmarks/proto/bench.proto b/benchmarks/proto/bench.proto
index d94056a44..ce13eeace 100644
--- a/benchmarks/proto/bench.proto
+++ b/benchmarks/proto/bench.proto
@@ -66,11 +66,19 @@ message Sample {
   string string = 22;
 }
 
+message SampleList {
+  repeated Sample sample_list = 1;
+}
+
 message MediaContent {
   Media media = 1;
   repeated Image images = 2;
 }
 
+message MediaContentList {
+  repeated MediaContent media_content_list = 1;
+}
+
 message Media {
   string uri = 1;
   optional string title = 2;
diff --git a/benchmarks/rust_benchmark/benches/threaded_bench.rs 
b/benchmarks/rust_benchmark/benches/threaded_bench.rs
index 404d7720a..b2a2710ab 100644
--- a/benchmarks/rust_benchmark/benches/threaded_bench.rs
+++ b/benchmarks/rust_benchmark/benches/threaded_bench.rs
@@ -27,14 +27,23 @@ use pprof::criterion::{Output, PProfProfiler};
 
 #[derive(Debug, ForyObject)]
 pub struct UserSessionMetrics {
+    #[fory(id = 0)]
     pub request_count: u64,
+    #[fory(id = 1)]
     pub unique_ip_count: u64,
+    #[fory(id = 2)]
     pub unique_user_agent_count: u64,
+    #[fory(id = 3)]
     pub unique_url_count: u64,
+    #[fory(id = 4)]
     pub unique_resource_count: u64,
+    #[fory(id = 5)]
     pub active_duration_secs: u64,
+    #[fory(id = 6)]
     pub first_seen_time: u64,
+    #[fory(id = 7)]
     pub last_seen_time: u64,
+    #[fory(id = 8)]
     pub updated_at: u64,
 }
 
diff --git a/benchmarks/rust_benchmark/run.sh b/benchmarks/rust_benchmark/run.sh
new file mode 100644
index 000000000..6bc062731
--- /dev/null
+++ b/benchmarks/rust_benchmark/run.sh
@@ -0,0 +1,202 @@
+#!/bin/bash
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+set -e
+export ENABLE_FORY_DEBUG_OUTPUT=0
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+cd "$SCRIPT_DIR"
+
+# Output directory for results
+OUTPUT_DIR="$SCRIPT_DIR/results"
+mkdir -p "$OUTPUT_DIR"
+
+# Default values
+DATA_FILTER=""
+SERIALIZER_FILTER=""
+CUSTOM_FILTER=""
+GENERATE_REPORT=true
+
+usage() {
+    echo "Usage: $0 [options]"
+    echo ""
+    echo "Build and run Rust benchmarks (criterion)"
+    echo ""
+    echo "Options:"
+    echo "  --data <type>       Filter by data type: simple_struct, 
simple_list, simple_map,"
+    echo "                      person, company, ecommerce_data, system_data, 
all"
+    echo "  --serializer <name> Filter by serializer: fory, json, protobuf"
+    echo "  --filter <regex>    Custom criterion filter regex (overrides 
--data/--serializer)"
+    echo "  --no-report         Skip report generation"
+    echo "  -h, --help          Show this help message"
+    echo ""
+    echo "Examples:"
+    echo "  $0                               # Default: 
simple_struct/ecommerce_data/system_data"
+    echo "  $0 --data simple_struct          # Only simple_struct benchmarks"
+    echo "  $0 --data ecommerce_data,system_data"
+    echo "  $0 --serializer fory             # Only Fory benchmarks (all data 
types)"
+    echo "  $0 --data simple_struct --serializer json"
+    echo "  $0 --filter 'simple_struct|person'"
+    exit 0
+}
+
+while [[ $# -gt 0 ]]; do
+    case $1 in
+        --data)
+            DATA_FILTER="$2"
+            shift 2
+            ;;
+        --serializer)
+            SERIALIZER_FILTER="$2"
+            shift 2
+            ;;
+        --filter)
+            CUSTOM_FILTER="$2"
+            shift 2
+            ;;
+        --no-report)
+            GENERATE_REPORT=false
+            shift
+            ;;
+        -h|--help)
+            usage
+            ;;
+        *)
+            echo "Unknown option: $1"
+            usage
+            ;;
+    esac
+done
+
+normalize_data_filter() {
+    local input="$1"
+    if [[ -z "$input" ]]; then
+        echo ""
+        return
+    fi
+    if [[ "$input" == "all" ]]; then
+        echo ""
+        return
+    fi
+    local result=()
+    IFS=',' read -ra parts <<< "$input"
+    for part in "${parts[@]}"; do
+        case "$part" in
+            simple_struct|struct) result+=("simple_struct") ;;
+            simple_list|list) result+=("simple_list") ;;
+            simple_map|map) result+=("simple_map") ;;
+            person) result+=("person") ;;
+            company) result+=("company") ;;
+            ecommerce_data|ecommerce|ecommerce-data) 
result+=("ecommerce_data") ;;
+            system_data|system|system-data) result+=("system_data") ;;
+            *)
+                echo "Unknown data type: $part"
+                exit 1
+                ;;
+        esac
+    done
+    local joined
+    joined="$(IFS='|'; echo "${result[*]}")"
+    echo "$joined"
+}
+
+build_filter() {
+    if [[ -n "$CUSTOM_FILTER" ]]; then
+        echo "$CUSTOM_FILTER"
+        return
+    fi
+
+    local data_regex
+    if [[ -n "$DATA_FILTER" ]]; then
+        data_regex="$(normalize_data_filter "$DATA_FILTER")"
+    else
+        data_regex="simple_struct|ecommerce_data|system_data"
+    fi
+
+    if [[ -n "$SERIALIZER_FILTER" ]]; then
+        case "$SERIALIZER_FILTER" in
+            fory|json|protobuf)
+                ;;
+            *)
+                echo "Unknown serializer: $SERIALIZER_FILTER"
+                exit 1
+                ;;
+        esac
+    fi
+
+    if [[ -n "$data_regex" && -n "$SERIALIZER_FILTER" ]]; then
+        echo "(${data_regex})/${SERIALIZER_FILTER}_"
+    elif [[ -n "$data_regex" ]]; then
+        echo "(${data_regex})"
+    elif [[ -n "$SERIALIZER_FILTER" ]]; then
+        echo "${SERIALIZER_FILTER}_"
+    else
+        echo ""
+    fi
+}
+
+FILTER_REGEX="$(build_filter)"
+
+LOG_FILE="$OUTPUT_DIR/cargo_bench.log"
+BENCH_CMD=(cargo bench --bench serialization_bench)
+if [[ -n "$FILTER_REGEX" ]]; then
+    BENCH_CMD+=(-- "$FILTER_REGEX")
+fi
+
+echo "============================================"
+echo "Fory Rust Benchmark"
+echo "============================================"
+if [[ -n "$FILTER_REGEX" ]]; then
+    echo "Filter: $FILTER_REGEX"
+else
+    echo "Filter: (all benchmarks)"
+fi
+echo "Log: $LOG_FILE"
+echo ""
+
+echo "============================================"
+echo "Running benchmarks..."
+echo "============================================"
+echo "Running: ${BENCH_CMD[*]}"
+echo ""
+"${BENCH_CMD[@]}" 2>&1 | tee "$LOG_FILE"
+
+if $GENERATE_REPORT; then
+    echo ""
+    echo "============================================"
+    echo "Generating report..."
+    echo "============================================"
+    if command -v python3 &> /dev/null; then
+        python3 "$SCRIPT_DIR/benchmark_report.py" --log-file "$LOG_FILE" 
--output-dir "$OUTPUT_DIR" || \
+            echo "Warning: Report generation failed. Install matplotlib and 
numpy for reports."
+    elif command -v python &> /dev/null; then
+        python "$SCRIPT_DIR/benchmark_report.py" --log-file "$LOG_FILE" 
--output-dir "$OUTPUT_DIR" || \
+            echo "Warning: Report generation failed. Install matplotlib and 
numpy for reports."
+    else
+        echo "Warning: Python not found. Skipping report generation."
+    fi
+fi
+
+echo ""
+echo "============================================"
+echo "Benchmark complete!"
+echo "============================================"
+echo "Results saved to: $OUTPUT_DIR/"
+echo "  - cargo_bench.log"
+if $GENERATE_REPORT; then
+    echo "  - REPORT.md and plots (if dependencies are available)"
+fi
diff --git a/benchmarks/rust_benchmark/src/models/complex.rs 
b/benchmarks/rust_benchmark/src/models/complex.rs
index e70003b36..2ea4ca589 100644
--- a/benchmarks/rust_benchmark/src/models/complex.rs
+++ b/benchmarks/rust_benchmark/src/models/complex.rs
@@ -24,47 +24,73 @@ use std::collections::HashMap;
 // Fory models
 #[derive(ForyObject, Debug, Clone, PartialEq, Default)]
 pub struct ForyProduct {
+    #[fory(id = 0)]
     pub id: String,
+    #[fory(id = 1)]
     pub name: String,
+    #[fory(id = 2)]
     pub price: f64,
+    #[fory(id = 3)]
     pub categories: Vec<String>,
+    #[fory(id = 4)]
     pub attributes: HashMap<String, String>,
 }
 
 #[derive(ForyObject, Debug, Clone, PartialEq)]
 pub struct ForyOrderItem {
+    #[fory(id = 0)]
     pub product: ForyProduct,
+    #[fory(id = 1)]
     pub quantity: i32,
+    #[fory(id = 2)]
     pub unit_price: f64,
+    #[fory(id = 3)]
     pub customizations: HashMap<String, String>,
 }
 
 #[derive(ForyObject, Debug, Clone, PartialEq, Default)]
 pub struct ForyCustomer {
+    #[fory(id = 0)]
     pub id: String,
+    #[fory(id = 1)]
     pub name: String,
+    #[fory(id = 2)]
     pub email: String,
+    #[fory(id = 3)]
     pub phone_numbers: Vec<String>,
+    #[fory(id = 4)]
     pub preferences: HashMap<String, String>,
+    #[fory(id = 5)]
     pub member_since: NaiveDateTime,
 }
 
 #[derive(ForyObject, Debug, Clone, PartialEq)]
 pub struct ForyOrder {
+    #[fory(id = 0)]
     pub id: String,
+    #[fory(id = 1)]
     pub customer: ForyCustomer,
+    #[fory(id = 2)]
     pub items: Vec<ForyOrderItem>,
+    #[fory(id = 3)]
     pub total_amount: f64,
+    #[fory(id = 4)]
     pub status: String,
+    #[fory(id = 5)]
     pub order_date: NaiveDateTime,
+    #[fory(id = 6)]
     pub metadata: HashMap<String, String>,
 }
 
 #[derive(ForyObject, Debug, Clone, PartialEq)]
 pub struct ECommerceData {
+    #[fory(id = 0)]
     pub orders: Vec<ForyOrder>,
+    #[fory(id = 1)]
     pub customers: Vec<ForyCustomer>,
+    #[fory(id = 2)]
     pub products: Vec<ForyProduct>,
+    #[fory(id = 3)]
     pub order_lookup: HashMap<String, ForyOrder>,
 }
 
diff --git a/benchmarks/rust_benchmark/src/models/medium.rs 
b/benchmarks/rust_benchmark/src/models/medium.rs
index 1a718bc16..d2d1a40f9 100644
--- a/benchmarks/rust_benchmark/src/models/medium.rs
+++ b/benchmarks/rust_benchmark/src/models/medium.rs
@@ -24,27 +24,41 @@ use std::collections::HashMap;
 // Fory models
 #[derive(ForyObject, Debug, Clone, PartialEq, Default)]
 pub struct ForyAddress {
+    #[fory(id = 0)]
     pub street: String,
+    #[fory(id = 1)]
     pub city: String,
+    #[fory(id = 2)]
     pub country: String,
+    #[fory(id = 3)]
     pub zip_code: String,
 }
 
 #[derive(ForyObject, Debug, Clone, PartialEq)]
 pub struct Person {
+    #[fory(id = 0)]
     pub name: String,
+    #[fory(id = 1)]
     pub age: i32,
+    #[fory(id = 2)]
     pub address: ForyAddress,
+    #[fory(id = 3)]
     pub hobbies: Vec<String>,
+    #[fory(id = 4)]
     pub metadata: HashMap<String, String>,
+    #[fory(id = 5)]
     pub created_at: NaiveDateTime,
 }
 
 #[derive(ForyObject, Debug, Clone, PartialEq)]
 pub struct Company {
+    #[fory(id = 0)]
     pub name: String,
+    #[fory(id = 1)]
     pub employees: Vec<Person>,
+    #[fory(id = 2)]
     pub offices: HashMap<String, ForyAddress>,
+    #[fory(id = 3)]
     pub is_public: bool,
 }
 
diff --git a/benchmarks/rust_benchmark/src/models/realworld.rs 
b/benchmarks/rust_benchmark/src/models/realworld.rs
index 854010e92..fdc5dcf11 100644
--- a/benchmarks/rust_benchmark/src/models/realworld.rs
+++ b/benchmarks/rust_benchmark/src/models/realworld.rs
@@ -24,42 +24,67 @@ use std::collections::HashMap;
 // Fory models
 #[derive(ForyObject, Debug, Clone, PartialEq)]
 pub struct ForyLogEntry {
+    #[fory(id = 0)]
     pub id: String,
+    #[fory(id = 1)]
     pub level: i32, // 0=DEBUG, 1=INFO, 2=WARN, 3=ERROR, 4=FATAL
+    #[fory(id = 2)]
     pub message: String,
+    #[fory(id = 3)]
     pub service: String,
+    #[fory(id = 4)]
     pub timestamp: NaiveDateTime,
+    #[fory(id = 5)]
     pub context: HashMap<String, String>,
+    #[fory(id = 6)]
     pub tags: Vec<String>,
+    #[fory(id = 7)]
     pub duration_ms: f64,
 }
 
 #[derive(ForyObject, Debug, Clone, PartialEq)]
 pub struct ForyUserProfile {
+    #[fory(id = 0)]
     pub user_id: String,
+    #[fory(id = 1)]
     pub username: String,
+    #[fory(id = 2)]
     pub email: String,
+    #[fory(id = 3)]
     pub preferences: HashMap<String, String>,
+    #[fory(id = 4)]
     pub permissions: Vec<String>,
+    #[fory(id = 5)]
     pub last_login: NaiveDateTime,
+    #[fory(id = 6)]
     pub is_active: bool,
 }
 
 #[derive(ForyObject, Debug, Clone, PartialEq)]
 pub struct ForyAPIMetrics {
+    #[fory(id = 0)]
     pub endpoint: String,
+    #[fory(id = 1)]
     pub request_count: i64,
+    #[fory(id = 2)]
     pub avg_response_time: f64,
+    #[fory(id = 3)]
     pub error_count: i64,
+    #[fory(id = 4)]
     pub status_codes: HashMap<String, i64>,
+    #[fory(id = 5)]
     pub measured_at: NaiveDateTime,
 }
 
 #[derive(ForyObject, Debug, Clone, PartialEq)]
 pub struct SystemData {
+    #[fory(id = 0)]
     pub logs: Vec<ForyLogEntry>,
+    #[fory(id = 1)]
     pub users: Vec<ForyUserProfile>,
+    #[fory(id = 2)]
     pub metrics: Vec<ForyAPIMetrics>,
+    #[fory(id = 3)]
     pub system_info: HashMap<String, String>,
 }
 
diff --git a/benchmarks/rust_benchmark/src/models/simple.rs 
b/benchmarks/rust_benchmark/src/models/simple.rs
index 4635721d8..eb6439940 100644
--- a/benchmarks/rust_benchmark/src/models/simple.rs
+++ b/benchmarks/rust_benchmark/src/models/simple.rs
@@ -23,21 +23,29 @@ use std::collections::HashMap;
 // Fory models
 #[derive(ForyObject, Debug, Clone, PartialEq)]
 pub struct SimpleStruct {
+    #[fory(id = 0)]
     pub id: i32,
+    #[fory(id = 1)]
     pub name: String,
+    #[fory(id = 2)]
     pub active: bool,
+    #[fory(id = 3)]
     pub score: f64,
 }
 
 #[derive(ForyObject, Debug, Clone, PartialEq)]
 pub struct SimpleList {
+    #[fory(id = 0)]
     pub numbers: Vec<i32>,
+    #[fory(id = 1)]
     pub names: Vec<String>,
 }
 
 #[derive(ForyObject, Debug, Clone, PartialEq)]
 pub struct SimpleMap {
+    #[fory(id = 0)]
     pub string_to_int: HashMap<String, i32>,
+    #[fory(id = 1)]
     pub int_to_string: HashMap<i32, String>,
 }
 
diff --git a/cpp/fory/serialization/context.cc 
b/cpp/fory/serialization/context.cc
index 5cc78b7a2..12c22deb7 100644
--- a/cpp/fory/serialization/context.cc
+++ b/cpp/fory/serialization/context.cc
@@ -60,7 +60,7 @@ static const std::vector<MetaEncoding> k_type_name_encodings 
= {
 WriteContext::WriteContext(const Config &config,
                            std::unique_ptr<TypeResolver> type_resolver)
     : buffer_(), config_(&config), type_resolver_(std::move(type_resolver)),
-      current_dyn_depth_(0) {}
+      current_dyn_depth_(0), write_type_info_index_map_(8) {}
 
 WriteContext::~WriteContext() = default;
 
@@ -75,17 +75,51 @@ WriteContext::write_type_meta(const std::type_index 
&type_id) {
 }
 
 void WriteContext::write_type_meta(const TypeInfo *type_info) {
-  auto it = write_type_info_index_map_.find(type_info);
-  if (it != write_type_info_index_map_.end()) {
+  const uint64_t key =
+      static_cast<uint64_t>(reinterpret_cast<uintptr_t>(type_info));
+  if (!type_info_index_map_active_) {
+    if (!has_first_type_info_) {
+      has_first_type_info_ = true;
+      first_type_info_ = type_info;
+      buffer_.write_uint8(0); // (index << 1), index=0
+      buffer_.write_bytes(type_info->type_def.data(),
+                          type_info->type_def.size());
+      return;
+    }
+    if (type_info == first_type_info_) {
+      buffer_.write_uint8(1); // (index << 1) | 1, index=0
+      return;
+    }
+    type_info_index_map_active_ = true;
+    write_type_info_index_map_.clear();
+    const uint64_t first_key =
+        static_cast<uint64_t>(reinterpret_cast<uintptr_t>(first_type_info_));
+    write_type_info_index_map_.put(first_key, 0);
+  } else if (type_info == first_type_info_) {
+    buffer_.write_uint8(1); // (index << 1) | 1, index=0
+    return;
+  }
+
+  if (auto *entry = write_type_info_index_map_.find(key)) {
     // Reference to previously written type: (index << 1) | 1, LSB=1
-    buffer_.write_var_uint32(static_cast<uint32_t>((it->second << 1) | 1));
+    uint32_t marker = static_cast<uint32_t>((entry->value << 1) | 1);
+    if (marker < 0x80) {
+      buffer_.write_uint8(static_cast<uint8_t>(marker));
+    } else {
+      buffer_.write_var_uint32(marker);
+    }
     return;
   }
 
   // New type: index << 1, LSB=0, followed by TypeDef bytes inline
-  size_t index = write_type_info_index_map_.size();
-  buffer_.write_var_uint32(static_cast<uint32_t>(index << 1));
-  write_type_info_index_map_[type_info] = index;
+  uint32_t index = static_cast<uint32_t>(write_type_info_index_map_.size());
+  uint32_t marker = static_cast<uint32_t>(index << 1);
+  if (marker < 0x80) {
+    buffer_.write_uint8(static_cast<uint8_t>(marker));
+  } else {
+    buffer_.write_var_uint32(marker);
+  }
+  write_type_info_index_map_.put(key, index);
 
   // write TypeDef bytes inline
   buffer_.write_bytes(type_info->type_def.data(), type_info->type_def.size());
@@ -368,6 +402,9 @@ void WriteContext::reset() {
   ref_writer_.reset();
   // Clear meta map for streaming TypeMeta (size is used as counter)
   write_type_info_index_map_.clear();
+  first_type_info_ = nullptr;
+  has_first_type_info_ = false;
+  type_info_index_map_active_ = false;
   current_dyn_depth_ = 0;
   // reset buffer indices for reuse - no memory operations needed
   buffer_.writer_index(0);
@@ -460,12 +497,50 @@ Result<const TypeInfo *, Error> 
ReadContext::read_type_meta() {
   }
 
   // Check if we already parsed this type meta (cache lookup by header)
+  if (has_last_meta_header_ && meta_header == last_meta_header_) {
+    // Fast path: same header as last parsed
+    const TypeInfo *cached = last_meta_type_info_;
+    reading_type_infos_.push_back(cached);
+    if (cached && !cached->type_def.empty()) {
+      const size_t type_def_size = cached->type_def.size();
+      if (type_def_size >= sizeof(int64_t) &&
+          type_def_size <= std::numeric_limits<uint32_t>::max()) {
+        Error skip_error;
+        buffer_->skip(static_cast<uint32_t>(type_def_size - sizeof(int64_t)),
+                      skip_error);
+        if (FORY_PREDICT_FALSE(!skip_error.ok())) {
+          return Unexpected(std::move(skip_error));
+        }
+        return cached;
+      }
+    }
+    FORY_RETURN_NOT_OK(TypeMeta::skip_bytes(*buffer_, meta_header));
+    return cached;
+  }
+
   auto cache_it = parsed_type_infos_.find(meta_header);
   if (cache_it != parsed_type_infos_.end()) {
     // Found in cache - reuse and skip the bytes
-    reading_type_infos_.push_back(cache_it->second);
+    const TypeInfo *cached = cache_it->second;
+    reading_type_infos_.push_back(cached);
+    has_last_meta_header_ = true;
+    last_meta_header_ = meta_header;
+    last_meta_type_info_ = cached;
+    if (cached && !cached->type_def.empty()) {
+      const size_t type_def_size = cached->type_def.size();
+      if (type_def_size >= sizeof(int64_t) &&
+          type_def_size <= std::numeric_limits<uint32_t>::max()) {
+        Error skip_error;
+        buffer_->skip(static_cast<uint32_t>(type_def_size - sizeof(int64_t)),
+                      skip_error);
+        if (FORY_PREDICT_FALSE(!skip_error.ok())) {
+          return Unexpected(std::move(skip_error));
+        }
+        return cached;
+      }
+    }
     FORY_RETURN_NOT_OK(TypeMeta::skip_bytes(*buffer_, meta_header));
-    return cache_it->second;
+    return cached;
   }
 
   // Not in cache - parse the TypeMeta
@@ -525,11 +600,16 @@ Result<const TypeInfo *, Error> 
ReadContext::read_type_meta() {
   const TypeInfo *raw_ptr = type_info.get();
 
   // Store in primary storage
-  owned_reading_type_infos_.push_back(std::move(type_info));
-
-  // Cache the parsed TypeInfo (with size limit to prevent OOM)
   if (parsed_type_infos_.size() < k_max_parsed_num_type_defs) {
+    cached_type_infos_.push_back(std::move(type_info));
+    raw_ptr = cached_type_infos_.back().get();
     parsed_type_infos_[meta_header] = raw_ptr;
+    has_last_meta_header_ = true;
+    last_meta_header_ = meta_header;
+    last_meta_type_info_ = raw_ptr;
+  } else {
+    owned_reading_type_infos_.push_back(std::move(type_info));
+    raw_ptr = owned_reading_type_infos_.back().get();
   }
 
   reading_type_infos_.push_back(raw_ptr);
@@ -607,7 +687,6 @@ void ReadContext::reset() {
   error_ = Error();
   ref_reader_.reset();
   reading_type_infos_.clear();
-  parsed_type_infos_.clear();
   owned_reading_type_infos_.clear();
   current_dyn_depth_ = 0;
   meta_string_table_.reset();
diff --git a/cpp/fory/serialization/context.h b/cpp/fory/serialization/context.h
index be11dd2dc..18e5e68bd 100644
--- a/cpp/fory/serialization/context.h
+++ b/cpp/fory/serialization/context.h
@@ -26,6 +26,7 @@
 #include "fory/type/type.h"
 #include "fory/util/buffer.h"
 #include "fory/util/error.h"
+#include "fory/util/flat_int_map.h"
 #include "fory/util/result.h"
 
 #include "absl/container/flat_hash_map.h"
@@ -331,7 +332,11 @@ private:
 
   // Meta sharing state (for streaming inline TypeMeta)
   // Maps TypeInfo* to index for reference tracking - uses map size as counter
-  absl::flat_hash_map<const TypeInfo *, size_t> write_type_info_index_map_;
+  util::FlatIntMap<uint64_t, uint32_t> write_type_info_index_map_;
+  // Fast path for the common single-type stream: avoid hash map lookups.
+  const TypeInfo *first_type_info_ = nullptr;
+  bool has_first_type_info_ = false;
+  bool type_info_index_map_active_ = false;
 };
 
 /// Read context for deserialization operations.
@@ -607,12 +612,18 @@ private:
   uint32_t current_dyn_depth_;
 
   // Meta sharing state (for compatible mode)
-  // Primary storage for TypeInfo objects created during deserialization
+  // Per-message storage for TypeInfo objects not cached across messages.
   std::vector<std::unique_ptr<TypeInfo>> owned_reading_type_infos_;
+  // Persistent cache storage for TypeInfo objects keyed by meta header.
+  std::vector<std::unique_ptr<TypeInfo>> cached_type_infos_;
   // Index-based access (pointers to owned_reading_type_infos_ or 
type_resolver)
   std::vector<const TypeInfo *> reading_type_infos_;
-  // Cache by meta_header (pointers to owned_reading_type_infos_)
+  // Cache by meta_header (pointers to cached_type_infos_)
   absl::flat_hash_map<int64_t, const TypeInfo *> parsed_type_infos_;
+  // Fast path for repeated type meta headers.
+  int64_t last_meta_header_ = 0;
+  const TypeInfo *last_meta_type_info_ = nullptr;
+  bool has_last_meta_header_ = false;
 
   // Dynamic meta strings used for named type/class info.
   meta::MetaStringTable meta_string_table_;
diff --git a/cpp/fory/serialization/struct_serializer.h 
b/cpp/fory/serialization/struct_serializer.h
index b864539b1..bb4a24bb8 100644
--- a/cpp/fory/serialization/struct_serializer.h
+++ b/cpp/fory/serialization/struct_serializer.h
@@ -681,6 +681,47 @@ template <typename T> struct CompileTimeFieldHelpers {
     }
   }
 
+  /// Returns true if the field needs per-field type info in compatible mode.
+  /// This matches write_single_field/read_single_field logic:
+  /// - struct/ext fields always write type info in compatible mode
+  /// - polymorphic fields write type info when dynamic_value is AUTO/TRUE
+  template <size_t Index>
+  static constexpr bool field_needs_type_info_in_compatible() {
+    if constexpr (FieldCount == 0) {
+      return false;
+    } else {
+      using PtrT = std::tuple_element_t<Index, FieldPtrs>;
+      using RawFieldType = meta::RemoveMemberPointerCVRefT<PtrT>;
+      using FieldType = unwrap_field_t<RawFieldType>;
+
+      constexpr TypeId field_type_id = Serializer<FieldType>::type_id;
+      constexpr bool is_struct = is_struct_type(field_type_id);
+      constexpr bool is_ext = is_ext_type(field_type_id);
+      constexpr bool is_polymorphic = field_type_id == TypeId::UNKNOWN;
+      constexpr int dynamic_val = field_dynamic_value<Index>();
+
+      constexpr bool polymorphic_write_type =
+          (dynamic_val == 1) || (dynamic_val == -1 && is_polymorphic);
+      return polymorphic_write_type || is_struct || is_ext;
+    }
+  }
+
+  template <size_t... Indices>
+  static constexpr bool
+  any_field_needs_type_info_in_compatible(std::index_sequence<Indices...>) {
+    if constexpr (FieldCount == 0) {
+      return false;
+    } else {
+      return (field_needs_type_info_in_compatible<Indices>() || ...);
+    }
+  }
+
+  /// True if it's safe to use schema-consistent fast path in compatible mode
+  /// (no struct/ext fields and no polymorphic fields that require type info).
+  static constexpr bool strict_compatible_safe =
+      !any_field_needs_type_info_in_compatible(
+          std::make_index_sequence<FieldCount>{});
+
   /// get the underlying field type (unwraps fory::field<> if present)
   template <size_t Index> struct UnwrappedFieldTypeHelper {
     using PtrT = std::tuple_element_t<Index, FieldPtrs>;
@@ -2750,6 +2791,53 @@ void read_struct_fields_impl(T &obj, ReadContext &ctx,
   (read_field_at_sorted_position<T, Indices>(obj, ctx), ...);
 }
 
+/// Read struct fields in sorted order using the fast primitive paths.
+/// Used when compatible mode is enabled but the remote schema matches locally.
+template <typename T, size_t... Indices>
+FORY_ALWAYS_INLINE void
+read_struct_fields_impl_fast(T &obj, ReadContext &ctx,
+                             std::index_sequence<Indices...>) {
+  using Helpers = CompileTimeFieldHelpers<T>;
+  constexpr size_t fixed_count = Helpers::leading_fixed_count;
+  constexpr size_t fixed_bytes = Helpers::leading_fixed_size_bytes;
+  constexpr size_t varint_count = Helpers::varint_count;
+  constexpr size_t total_count = sizeof...(Indices);
+
+  Buffer &buffer = ctx.buffer();
+
+  // Phase 1: Read leading fixed-size primitives if any
+  if constexpr (fixed_count > 0 && fixed_bytes > 0) {
+    // Pre-check bounds for all fixed-size fields at once
+    if (FORY_PREDICT_FALSE(buffer.reader_index() + fixed_bytes >
+                           buffer.size())) {
+      ctx.set_error(Error::buffer_out_of_bound(buffer.reader_index(),
+                                               fixed_bytes, buffer.size()));
+      return;
+    }
+    // Fast read fixed-size primitives
+    read_fixed_primitive_fields<T>(obj, buffer,
+                                   std::make_index_sequence<fixed_count>{});
+  }
+
+  // Phase 2: Read consecutive varint primitives (int32, int64) if any
+  if constexpr (varint_count > 0) {
+    // Track offset locally for batch varint reading
+    uint32_t offset = buffer.reader_index();
+    // Fast read varint primitives (bounds checking happens in
+    // get_var_uint32/64)
+    read_varint_primitive_fields<T, fixed_count>(
+        obj, buffer, offset, std::make_index_sequence<varint_count>{});
+    // Update reader_index once after all varints
+    buffer.reader_index(offset);
+  }
+
+  // Phase 3: Read remaining fields (if any) with normal path
+  constexpr size_t fast_count = fixed_count + varint_count;
+  if constexpr (fast_count < total_count) {
+    read_remaining_fields<T, fast_count, total_count>(obj, ctx);
+  }
+}
+
 /// Read struct fields with schema evolution (compatible mode)
 /// Reads fields in remote schema order, dispatching by field_id to local 
fields
 template <typename T, size_t... Indices>
@@ -3098,6 +3186,7 @@ struct Serializer<T, 
std::enable_if_t<is_fory_serializable_v<T>>> {
 
   static T read_compatible(ReadContext &ctx, const TypeInfo *remote_type_info) 
{
     // Read and verify struct version if enabled (matches write_data behavior)
+    const TypeInfo *local_type_info = nullptr;
     if (ctx.check_struct_version()) {
       int32_t read_version = ctx.buffer().read_int32(ctx.error());
       if (FORY_PREDICT_FALSE(ctx.has_error())) {
@@ -3109,7 +3198,7 @@ struct Serializer<T, 
std::enable_if_t<is_fory_serializable_v<T>>> {
         ctx.set_error(std::move(local_type_info_res).error());
         return T{};
       }
-      const TypeInfo *local_type_info = local_type_info_res.value();
+      local_type_info = local_type_info_res.value();
       if (!local_type_info->type_meta) {
         ctx.set_error(Error::type_error(
             "Type metadata not initialized for requested struct"));
@@ -3123,6 +3212,19 @@ struct Serializer<T, 
std::enable_if_t<is_fory_serializable_v<T>>> {
         ctx.set_error(std::move(version_res).error());
         return T{};
       }
+    } else {
+      auto local_type_info_res =
+          ctx.type_resolver().template get_type_info<T>();
+      if (!local_type_info_res.ok()) {
+        ctx.set_error(std::move(local_type_info_res).error());
+        return T{};
+      }
+      local_type_info = local_type_info_res.value();
+      if (!local_type_info->type_meta) {
+        ctx.set_error(Error::type_error(
+            "Type metadata not initialized for requested struct"));
+        return T{};
+      }
     }
 
     T obj{};
@@ -3136,6 +3238,29 @@ struct Serializer<T, 
std::enable_if_t<is_fory_serializable_v<T>>> {
       return T{};
     }
 
+    // Fast path: same schema hash, read fields in local sorted order.
+    if (local_type_info &&
+        remote_type_info->type_meta->hash == local_type_info->type_meta->hash) 
{
+      if constexpr (detail::CompileTimeFieldHelpers<
+                        T>::strict_compatible_safe) {
+        // Safe to use schema-consistent fast path (no per-field type info).
+        detail::read_struct_fields_impl_fast(
+            obj, ctx, std::make_index_sequence<field_count>{});
+        if (FORY_PREDICT_FALSE(ctx.has_error())) {
+          return T{};
+        }
+        return obj;
+      }
+
+      // Compatible fast path: same order, but allow per-field type info.
+      detail::read_struct_fields_impl_fast(
+          obj, ctx, std::make_index_sequence<field_count>{});
+      if (FORY_PREDICT_FALSE(ctx.has_error())) {
+        return T{};
+      }
+      return obj;
+    }
+
     // Use remote TypeMeta for schema evolution - field IDs already assigned
     detail::read_struct_fields_compatible(
         obj, ctx, remote_type_info->type_meta.get(),
diff --git a/cpp/fory/serialization/struct_test.cc 
b/cpp/fory/serialization/struct_test.cc
index bd2713fe6..f02b66402 100644
--- a/cpp/fory/serialization/struct_test.cc
+++ b/cpp/fory/serialization/struct_test.cc
@@ -80,6 +80,32 @@ struct ManyFieldsStruct {
   FORY_STRUCT(ManyFieldsStruct, b1, i8, i16, i32, i64, f32, f64, str);
 };
 
+struct FieldConfigTaggedStruct {
+  int32_t a;
+  int64_t b;
+  std::string c;
+
+  bool operator==(const FieldConfigTaggedStruct &other) const {
+    return a == other.a && b == other.b && c == other.c;
+  }
+  FORY_STRUCT(FieldConfigTaggedStruct, a, b, c);
+};
+
+struct FieldTagsTaggedStruct {
+  int32_t a;
+  int64_t b;
+  std::string c;
+
+  bool operator==(const FieldTagsTaggedStruct &other) const {
+    return a == other.a && b == other.b && c == other.c;
+  }
+  FORY_STRUCT(FieldTagsTaggedStruct, a, b, c);
+};
+
+FORY_FIELD_CONFIG(FieldConfigTaggedStruct, (a, fory::F().id(1)),
+                  (b, fory::F().id(2)), (c, fory::F().id(3)));
+FORY_FIELD_TAGS(FieldTagsTaggedStruct, (a, 1), (b, 2), (c, 3));
+
 class PrivateFieldsStruct {
 public:
   PrivateFieldsStruct() = default;
@@ -474,6 +500,28 @@ TEST(StructComprehensiveTest, ManyFieldsStruct) {
                                   -9223372036854775807LL - 1, -1.0f, -1.0, 
""});
 }
 
+TEST(StructComprehensiveTest, FieldTagsMatchFieldConfigSize) {
+  FieldConfigTaggedStruct config_obj{1, 2, "config"};
+  FieldTagsTaggedStruct tags_obj{1, 2, "config"};
+
+  auto fory_config =
+      Fory::builder().xlang(true).compatible(true).track_ref(false).build();
+  auto fory_tags =
+      Fory::builder().xlang(true).compatible(true).track_ref(false).build();
+
+  ASSERT_TRUE(fory_config.register_struct<FieldConfigTaggedStruct>(101).ok());
+  ASSERT_TRUE(fory_tags.register_struct<FieldTagsTaggedStruct>(101).ok());
+
+  auto config_bytes = fory_config.serialize(config_obj);
+  ASSERT_TRUE(config_bytes.ok())
+      << "Serialization failed: " << config_bytes.error().to_string();
+  auto tags_bytes = fory_tags.serialize(tags_obj);
+  ASSERT_TRUE(tags_bytes.ok())
+      << "Serialization failed: " << tags_bytes.error().to_string();
+
+  EXPECT_EQ(config_bytes->size(), tags_bytes->size());
+}
+
 TEST(StructComprehensiveTest, PrivateFieldsStruct) {
   test_roundtrip(PrivateFieldsStruct{42, "secret", {1, 2, 3}});
 }
diff --git a/cpp/fory/serialization/type_resolver.cc 
b/cpp/fory/serialization/type_resolver.cc
index 87d8003a8..a5c993017 100644
--- a/cpp/fory/serialization/type_resolver.cc
+++ b/cpp/fory/serialization/type_resolver.cc
@@ -160,10 +160,10 @@ Result<std::vector<uint8_t>, Error> FieldInfo::to_bytes() 
const {
     }
     encoded_name = std::move(encoded.bytes);
   }
-  size_t name_size =
-      use_tag_id ? static_cast<size_t>(field_id) + 1 : encoded_name.size();
+  const size_t size_field =
+      use_tag_id ? static_cast<size_t>(field_id) : encoded_name.size() - 1;
   uint8_t header =
-      (std::min(FIELD_NAME_SIZE_THRESHOLD, name_size - 1) << 2) & 0x3C;
+      (std::min(FIELD_NAME_SIZE_THRESHOLD, size_field) << 2) & 0x3C;
 
   if (field_type.track_ref) {
     header |= 1; // bit 0 for ref tracking
@@ -175,8 +175,8 @@ Result<std::vector<uint8_t>, Error> FieldInfo::to_bytes() 
const {
 
   buffer.write_uint8(header);
 
-  if (name_size - 1 >= FIELD_NAME_SIZE_THRESHOLD) {
-    buffer.write_var_uint32(name_size - 1 - FIELD_NAME_SIZE_THRESHOLD);
+  if (size_field >= FIELD_NAME_SIZE_THRESHOLD) {
+    buffer.write_var_uint32(size_field - FIELD_NAME_SIZE_THRESHOLD);
   }
 
   // write field type
@@ -209,15 +209,14 @@ Result<FieldInfo, Error> FieldInfo::from_bytes(Buffer 
&buffer) {
   bool use_tag_id = encoding_idx == 3;
   bool track_ref = (header & 0b01u) != 0;
   bool nullable = (header & 0b10u) != 0;
-  size_t name_size = ((header >> 2) & FIELD_NAME_SIZE_THRESHOLD);
-  if (name_size == FIELD_NAME_SIZE_THRESHOLD) {
+  size_t size_field = ((header >> 2) & FIELD_NAME_SIZE_THRESHOLD);
+  if (size_field == FIELD_NAME_SIZE_THRESHOLD) {
     uint32_t extra = buffer.read_var_uint32(error);
     if (FORY_PREDICT_FALSE(!error.ok())) {
       return Unexpected(std::move(error));
     }
-    name_size += extra;
+    size_field += extra;
   }
-  name_size += 1;
 
   // Read field type with nullable and track_ref from header
   FORY_TRY(field_type,
@@ -225,7 +224,7 @@ Result<FieldInfo, Error> FieldInfo::from_bytes(Buffer 
&buffer) {
 
   if (use_tag_id) {
     FieldInfo info("", std::move(field_type));
-    info.field_id = static_cast<int16_t>(name_size - 1);
+    info.field_id = static_cast<int16_t>(size_field);
     return info;
   }
 
@@ -236,6 +235,7 @@ Result<FieldInfo, Error> FieldInfo::from_bytes(Buffer 
&buffer) {
   // We mirror that here using MetaStringDecoder with '$' and '_' as
   // special characters (same as Encoders.FIELD_NAME_DECODER).
 
+  const size_t name_size = size_field + 1;
   std::vector<uint8_t> name_bytes(name_size);
   buffer.read_bytes(name_bytes.data(), static_cast<uint32_t>(name_size), 
error);
   if (FORY_PREDICT_FALSE(!error.ok())) {
diff --git a/cpp/fory/serialization/type_resolver.h 
b/cpp/fory/serialization/type_resolver.h
index 6bdf6521f..e3489b44a 100644
--- a/cpp/fory/serialization/type_resolver.h
+++ b/cpp/fory/serialization/type_resolver.h
@@ -713,6 +713,12 @@ constexpr int16_t compute_field_id() {
   if constexpr (is_fory_field_v<ActualFieldType>) {
     return field_tag_id_v<ActualFieldType>;
   }
+  if constexpr (::fory::detail::has_field_tags_v<T>) {
+    constexpr int16_t tag_id = ::fory::detail::GetFieldTagEntry<T, Index>::id;
+    if constexpr (tag_id >= 0) {
+      return tag_id;
+    }
+  }
   return -1;
 }
 
diff --git a/cpp/fory/serialization/xlang_test_main.cc 
b/cpp/fory/serialization/xlang_test_main.cc
index cf8e087f1..69ecaf49b 100644
--- a/cpp/fory/serialization/xlang_test_main.cc
+++ b/cpp/fory/serialization/xlang_test_main.cc
@@ -503,22 +503,6 @@ struct RefOuterSchemaConsistent {
   }
   FORY_STRUCT(RefOuterSchemaConsistent, inner1, inner2);
 };
-FORY_FIELD_TAGS(RefOuterSchemaConsistent, (inner1, 0, nullable, ref),
-                (inner2, 1, nullable, ref));
-// Verify field tags are correctly parsed
-static_assert(fory::detail::has_field_tags_v<RefOuterSchemaConsistent>,
-              "RefOuterSchemaConsistent should have field tags");
-static_assert(fory::detail::GetFieldTagEntry<RefOuterSchemaConsistent, 0>::id 
==
-                  0,
-              "inner1 should have id=0");
-static_assert(
-    fory::detail::GetFieldTagEntry<RefOuterSchemaConsistent, 0>::is_nullable ==
-        true,
-    "inner1 should be nullable");
-static_assert(
-    fory::detail::GetFieldTagEntry<RefOuterSchemaConsistent, 0>::track_ref ==
-        true,
-    "inner1 should have track_ref=true");
 
 // Inner struct for reference tracking test (COMPATIBLE mode)
 // Matches Java RefInnerCompatible with type ID 503
@@ -552,8 +536,6 @@ struct RefOuterCompatible {
   }
   FORY_STRUCT(RefOuterCompatible, inner1, inner2);
 };
-FORY_FIELD_TAGS(RefOuterCompatible, (inner1, 0, nullable, ref),
-                (inner2, 1, nullable, ref));
 
 // Element struct for collection element ref override test
 // Matches Java RefOverrideElement with type ID 701
@@ -603,7 +585,6 @@ struct CircularRefStruct {
   }
   FORY_STRUCT(CircularRefStruct, name, self_ref);
 };
-FORY_FIELD_TAGS(CircularRefStruct, (name, 0), (self_ref, 1, nullable, ref));
 
 // ============================================================================
 // Unsigned Number Test Types
diff --git a/go/fory/field_info.go b/go/fory/field_info.go
index c10766a84..fb92d0b1e 100644
--- a/go/fory/field_info.go
+++ b/go/fory/field_info.go
@@ -60,8 +60,9 @@ type FieldMeta struct {
        FixedSize int // 0 if not fixed-size, else 1/2/4/8
 
        // Pre-computed flags for serialization (computed at init time)
-       WriteType   bool // whether to write type info (true for struct fields 
in compatible mode)
-       HasGenerics bool // whether element types are known from TypeDef (for 
container fields)
+       WriteType      bool // whether to write type info (true for struct 
fields in compatible mode)
+       CachedTypeInfo *TypeInfo
+       HasGenerics    bool // whether element types are known from TypeDef 
(for container fields)
 
        // Tag-based configuration (from fory struct tags)
        TagID          int  // -1 = use field name, >=0 = use tag ID
diff --git a/go/fory/fory.go b/go/fory/fory.go
index 97b9dc4ae..09a0e3c6d 100644
--- a/go/fory/fory.go
+++ b/go/fory/fory.go
@@ -134,7 +134,7 @@ func New(opts ...Option) *Fory {
        // Initialize meta context if compatible mode is enabled
        if f.config.Compatible {
                f.metaContext = &MetaContext{
-                       typeMap:               make(map[reflect.Type]uint32),
+                       typeMap:               make(map[uintptr]uint32),
                        readTypeInfos:         make([]*TypeInfo, 0),
                        scopedMetaShareEnable: true,
                }
diff --git a/go/fory/pointer.go b/go/fory/pointer.go
index 50a9809b0..b30a15f53 100644
--- a/go/fory/pointer.go
+++ b/go/fory/pointer.go
@@ -94,6 +94,47 @@ func (s *ptrToValueSerializer) Write(ctx *WriteContext, 
refMode RefMode, writeTy
        s.WriteData(ctx, value)
 }
 
+func (s *ptrToValueSerializer) writeWithTypeInfo(ctx *WriteContext, refMode 
RefMode, value reflect.Value, typeInfo *TypeInfo) {
+       switch refMode {
+       case RefModeTracking:
+               if value.IsNil() {
+                       ctx.Buffer().WriteInt8(NullFlag)
+                       return
+               }
+               refWritten, err := 
ctx.RefResolver().WriteRefOrNull(ctx.Buffer(), value)
+               if err != nil {
+                       ctx.SetError(FromError(err))
+                       return
+               }
+               if refWritten {
+                       return
+               }
+       case RefModeNullOnly:
+               if value.IsNil() {
+                       ctx.Buffer().WriteInt8(NullFlag)
+                       return
+               }
+               ctx.Buffer().WriteInt8(NotNullValueFlag)
+       case RefModeNone:
+               if value.IsNil() {
+                       zeroValue := reflect.New(value.Type().Elem()).Elem()
+                       if typeInfo == nil {
+                               s.Write(ctx, refMode, true, false, value)
+                               return
+                       }
+                       ctx.TypeResolver().WriteTypeInfo(ctx.Buffer(), 
typeInfo, ctx.Err())
+                       s.valueSerializer.WriteData(ctx, zeroValue)
+                       return
+               }
+       }
+       if typeInfo == nil {
+               s.Write(ctx, refMode, true, false, value)
+               return
+       }
+       ctx.TypeResolver().WriteTypeInfo(ctx.Buffer(), typeInfo, ctx.Err())
+       s.WriteData(ctx, value)
+}
+
 func (s *ptrToValueSerializer) ReadData(ctx *ReadContext, value reflect.Value) 
{
        // Check if value is already allocated (for circular reference handling)
        var newVal reflect.Value
diff --git a/go/fory/struct.go b/go/fory/struct.go
index 123f53b7f..2cd21991b 100644
--- a/go/fory/struct.go
+++ b/go/fory/struct.go
@@ -114,6 +114,36 @@ func (s *structSerializer) Write(ctx *WriteContext, 
refMode RefMode, writeType b
        s.WriteData(ctx, value)
 }
 
+func (s *structSerializer) writeWithTypeInfo(ctx *WriteContext, refMode 
RefMode, value reflect.Value, typeInfo *TypeInfo) {
+       switch refMode {
+       case RefModeTracking:
+               if value.Kind() == reflect.Ptr && value.IsNil() {
+                       ctx.buffer.WriteInt8(NullFlag)
+                       return
+               }
+               refWritten, err := ctx.RefResolver().WriteRefOrNull(ctx.buffer, 
value)
+               if err != nil {
+                       ctx.SetError(FromError(err))
+                       return
+               }
+               if refWritten {
+                       return
+               }
+       case RefModeNullOnly:
+               if value.Kind() == reflect.Ptr && value.IsNil() {
+                       ctx.buffer.WriteInt8(NullFlag)
+                       return
+               }
+               ctx.buffer.WriteInt8(NotNullValueFlag)
+       }
+       if typeInfo == nil {
+               s.Write(ctx, refMode, true, false, value)
+               return
+       }
+       ctx.TypeResolver().WriteTypeInfo(ctx.buffer, typeInfo, ctx.Err())
+       s.WriteData(ctx, value)
+}
+
 func (s *structSerializer) WriteData(ctx *WriteContext, value reflect.Value) {
        // Early error check - skip all intermediate checks for normal path 
performance
        if ctx.HasError() {
@@ -398,6 +428,9 @@ func (s *structSerializer) writeRemainingField(ctx 
*WriteContext, ptr unsafe.Poi
                }
                fieldValue := value.Field(field.Meta.FieldIndex)
                if field.Serializer != nil {
+                       if writeWithCachedTypeInfo(ctx, field, fieldValue) {
+                               return
+                       }
                        field.Serializer.Write(ctx, field.RefMode, 
field.Meta.WriteType, field.Meta.HasGenerics, fieldValue)
                } else {
                        ctx.WriteValue(fieldValue, RefModeTracking, true)
@@ -774,12 +807,31 @@ func (s *structSerializer) writeRemainingField(ctx 
*WriteContext, ptr unsafe.Poi
        // Fall back to serializer for other types
        fieldValue := value.Field(field.Meta.FieldIndex)
        if field.Serializer != nil {
+               if writeWithCachedTypeInfo(ctx, field, fieldValue) {
+                       return
+               }
                field.Serializer.Write(ctx, field.RefMode, 
field.Meta.WriteType, field.Meta.HasGenerics, fieldValue)
        } else {
                ctx.WriteValue(fieldValue, RefModeTracking, true)
        }
 }
 
+func writeWithCachedTypeInfo(ctx *WriteContext, field *FieldInfo, fieldValue 
reflect.Value) bool {
+       if field.Serializer == nil || !field.Meta.WriteType || 
field.Meta.CachedTypeInfo == nil {
+               return false
+       }
+       switch ser := field.Serializer.(type) {
+       case *structSerializer:
+               ser.writeWithTypeInfo(ctx, field.RefMode, fieldValue, 
field.Meta.CachedTypeInfo)
+               return true
+       case *ptrToValueSerializer:
+               ser.writeWithTypeInfo(ctx, field.RefMode, fieldValue, 
field.Meta.CachedTypeInfo)
+               return true
+       default:
+               return false
+       }
+}
+
 func loadFieldValue[T any](kind FieldKind, fieldPtr unsafe.Pointer, opt 
optionalInfo) (T, bool) {
        var zero T
        switch kind {
diff --git a/go/fory/struct_init.go b/go/fory/struct_init.go
index 8dc8eae5a..3b994b10f 100644
--- a/go/fory/struct_init.go
+++ b/go/fory/struct_init.go
@@ -373,6 +373,14 @@ func (s *structSerializer) initFields(typeResolver 
*TypeResolver) error {
                }
                // Pre-compute WriteType: true for struct fields in compatible 
mode
                writeType := typeResolver.Compatible() && 
isStructField(baseType)
+               var cachedTypeInfo *TypeInfo
+               if writeType {
+                       cachedType := baseType
+                       if cachedType.Kind() == reflect.Ptr {
+                               cachedType = cachedType.Elem()
+                       }
+                       cachedTypeInfo = 
typeResolver.getTypeInfoByType(cachedType)
+               }
 
                // Pre-compute DispatchId, with special handling for enum 
fields and pointer-to-numeric
                dispatchId := getDispatchIdFromTypeId(fieldTypeId, nullableFlag)
@@ -410,6 +418,7 @@ func (s *structSerializer) initFields(typeResolver 
*TypeResolver) error {
                                Nullable:       nullableFlag, // Use same logic 
as TypeDef's nullable flag for consistent ref handling
                                FieldIndex:     i,
                                WriteType:      writeType,
+                               CachedTypeInfo: cachedTypeInfo,
                                HasGenerics:    isCollectionType(fieldTypeId), 
// Container fields have declared element types
                                OptionalInfo:   optionalInfo,
                                TagID:          foryTag.ID,
@@ -847,6 +856,14 @@ func (s *structSerializer) 
initFieldsFromTypeDef(typeResolver *TypeResolver) err
                }
                // Pre-compute WriteType: true for struct fields in compatible 
mode
                writeType := typeResolver.Compatible() && 
isStructField(baseType)
+               var cachedTypeInfo *TypeInfo
+               if writeType {
+                       cachedType := baseType
+                       if cachedType.Kind() == reflect.Ptr {
+                               cachedType = cachedType.Elem()
+                       }
+                       cachedTypeInfo = 
typeResolver.getTypeInfoByType(cachedType)
+               }
 
                // Pre-compute DispatchId, with special handling for 
pointer-to-numeric and enum fields
                // IMPORTANT: For compatible mode reading, we must use the 
REMOTE nullable flag
@@ -905,17 +922,18 @@ func (s *structSerializer) 
initFieldsFromTypeDef(typeResolver *TypeResolver) err
                        Kind:       fieldKind,
                        Serializer: fieldSerializer,
                        Meta: &FieldMeta{
-                               Name:         fieldName,
-                               Type:         fieldType,
-                               TypeId:       fieldTypeId,
-                               Nullable:     def.nullable, // Use remote 
nullable flag
-                               FieldIndex:   fieldIndex,
-                               FieldDef:     def, // Save original FieldDef 
for skipping
-                               WriteType:    writeType,
-                               HasGenerics:  isCollectionType(fieldTypeId), // 
Container fields have declared element types
-                               OptionalInfo: optionalInfo,
-                               TagID:        def.tagID,
-                               HasForyTag:   def.tagID >= 0,
+                               Name:           fieldName,
+                               Type:           fieldType,
+                               TypeId:         fieldTypeId,
+                               Nullable:       def.nullable, // Use remote 
nullable flag
+                               FieldIndex:     fieldIndex,
+                               FieldDef:       def, // Save original FieldDef 
for skipping
+                               WriteType:      writeType,
+                               CachedTypeInfo: cachedTypeInfo,
+                               HasGenerics:    isCollectionType(fieldTypeId), 
// Container fields have declared element types
+                               OptionalInfo:   optionalInfo,
+                               TagID:          def.tagID,
+                               HasForyTag:     def.tagID >= 0,
                        },
                }
                fields = append(fields, fieldInfo)
diff --git a/go/fory/type_def.go b/go/fory/type_def.go
index 9a0d41621..f2bca01a6 100644
--- a/go/fory/type_def.go
+++ b/go/fory/type_def.go
@@ -54,6 +54,7 @@ type TypeDef struct {
        fieldDefs      []FieldDef
        encoded        []byte
        type_          reflect.Type
+       cachedTypeInfo *TypeInfo
 }
 
 func NewTypeDef(typeId uint32, userTypeId uint32, nsName, typeName 
*MetaStringBytes, registerByName, compressed bool, fieldDefs []FieldDef) 
*TypeDef {
@@ -264,6 +265,18 @@ func (td *TypeDef) buildTypeInfoWithResolver(resolver 
*TypeResolver) (TypeInfo,
        return info, nil
 }
 
+func (td *TypeDef) getOrBuildTypeInfo(resolver *TypeResolver) (*TypeInfo, 
error) {
+       if td.cachedTypeInfo != nil {
+               return td.cachedTypeInfo, nil
+       }
+       info, err := td.buildTypeInfoWithResolver(resolver)
+       if err != nil {
+               return nil, err
+       }
+       td.cachedTypeInfo = &info
+       return td.cachedTypeInfo, nil
+}
+
 func readTypeDef(fory *Fory, buffer *ByteBuffer, header int64, err *Error) 
*TypeDef {
        td, decodeErr := decodeTypeDef(fory, buffer, header)
        if decodeErr != nil {
diff --git a/go/fory/type_resolver.go b/go/fory/type_resolver.go
index ebb4c416f..bd49a03e1 100644
--- a/go/fory/type_resolver.go
+++ b/go/fory/type_resolver.go
@@ -1460,36 +1460,96 @@ func (r *TypeResolver) WriteTypeInfo(buffer 
*ByteBuffer, typeInfo *TypeInfo, err
 
 func (r *TypeResolver) writeSharedTypeMeta(buffer *ByteBuffer, typeInfo 
*TypeInfo, err *Error) {
        context := r.fory.MetaContext()
-       typ := typeInfo.Type
-
-       if index, exists := context.typeMap[typ]; exists {
-               // Reference to previously written type: (index << 1) | 1, LSB=1
-               buffer.WriteVarUint32((index << 1) | 1)
-               return
-       }
-
-       // New type: index << 1, LSB=0, followed by TypeDef bytes inline
-       newIndex := uint32(len(context.typeMap))
-       buffer.WriteVarUint32(newIndex << 1)
-       context.typeMap[typ] = newIndex
-
-       // Only build TypeDef for struct types - enums don't have field 
definitions
-       actualType := typ
-       if actualType.Kind() == reflect.Ptr {
-               actualType = actualType.Elem()
+       key := typePointer(typeInfo.Type)
+       writeTypeDefInline := func() {
+               // Only build TypeDef for struct types - enums don't have field 
definitions
+               actualType := typeInfo.Type
+               if actualType.Kind() == reflect.Ptr {
+                       actualType = actualType.Elem()
+               }
+               if actualType.Kind() == reflect.Struct {
+                       typeDef, typeDefErr := r.getTypeDef(typeInfo.Type, true)
+                       if typeDefErr != nil {
+                               err.SetError(typeDefErr)
+                               return
+                       }
+                       // Write TypeDef bytes inline
+                       typeDef.writeTypeDef(buffer, err)
+               }
        }
-       if actualType.Kind() == reflect.Struct {
+       writeTypeDefWithZeroMarker := func() {
+               actualType := typeInfo.Type
+               if actualType.Kind() == reflect.Ptr {
+                       actualType = actualType.Elem()
+               }
+               if actualType.Kind() != reflect.Struct {
+                       buffer.WriteUint8(0)
+                       return
+               }
                typeDef, typeDefErr := r.getTypeDef(typeInfo.Type, true)
                if typeDefErr != nil {
                        err.SetError(typeDefErr)
                        return
                }
-               // Write TypeDef bytes inline
+               buffer.WriteUint8(0)
                typeDef.writeTypeDef(buffer, err)
        }
+       if !context.typeMapActive {
+               if !context.hasFirstType {
+                       context.hasFirstType = true
+                       context.firstTypePtr = key
+                       // New type: index << 1, LSB=0, followed by TypeDef 
bytes inline
+                       writeTypeDefWithZeroMarker()
+                       return
+               }
+               if key == context.firstTypePtr {
+                       // Reference to first type: (0 << 1) | 1
+                       buffer.WriteUint8(1)
+                       return
+               }
+               context.typeMapActive = true
+               if context.typeMap == nil {
+                       context.typeMap = make(map[uintptr]uint32, 8)
+               } else if len(context.typeMap) != 0 {
+                       for k := range context.typeMap {
+                               delete(context.typeMap, k)
+                       }
+               }
+               context.typeMap[context.firstTypePtr] = 0
+       } else if key == context.firstTypePtr {
+               buffer.WriteUint8(1)
+               return
+       }
+
+       if index, exists := context.typeMap[key]; exists {
+               // Reference to previously written type: (index << 1) | 1, LSB=1
+               marker := (index << 1) | 1
+               if marker < 0x80 {
+                       buffer.WriteUint8(uint8(marker))
+               } else {
+                       buffer.WriteVarUint32(marker)
+               }
+               return
+       }
+
+       // New type: index << 1, LSB=0, followed by TypeDef bytes inline
+       newIndex := uint32(len(context.typeMap))
+       marker := newIndex << 1
+       if marker < 0x80 {
+               buffer.WriteUint8(uint8(marker))
+       } else {
+               buffer.WriteVarUint32(marker)
+       }
+       context.typeMap[key] = newIndex
+       writeTypeDefInline()
 }
 
 func (r *TypeResolver) getTypeDef(typ reflect.Type, create bool) (*TypeDef, 
error) {
+       // Normalize pointer types to their element type for consistent caching.
+       if typ.Kind() == reflect.Ptr {
+               typ = typ.Elem()
+       }
+
        if existingTypeDef, exists := r.typeToTypeDef[typ]; exists {
                return existingTypeDef, nil
        }
@@ -1498,10 +1558,6 @@ func (r *TypeResolver) getTypeDef(typ reflect.Type, 
create bool) (*TypeDef, erro
                return nil, fmt.Errorf("TypeDef not found for type %s", typ)
        }
 
-       // don't create TypeDef for pointer types, we create TypeDef for its 
element type instead.
-       if typ.Kind() == reflect.Ptr {
-               typ = typ.Elem()
-       }
        zero := reflect.Zero(typ)
        typeDef, err := buildTypeDef(r.fory, zero)
        if err != nil {
@@ -1563,14 +1619,14 @@ func (r *TypeResolver) readSharedTypeMeta(buffer 
*ByteBuffer, err *Error) *TypeI
                td = newTd
        }
 
-       typeInfo, typeInfoErr := td.buildTypeInfoWithResolver(r)
+       typeInfo, typeInfoErr := td.getOrBuildTypeInfo(r)
        if typeInfoErr != nil {
                err.SetError(typeInfoErr)
                return nil
        }
 
-       context.readTypeInfos = append(context.readTypeInfos, &typeInfo)
-       return &typeInfo
+       context.readTypeInfos = append(context.readTypeInfos, typeInfo)
+       return typeInfo
 }
 
 func (r *TypeResolver) createSerializer(type_ reflect.Type, mapInStruct bool) 
(s Serializer, err error) {
@@ -2341,9 +2397,12 @@ var ErrTypeMismatch = errors.New("fory: type ID 
mismatch")
 
 // MetaContext holds metadata for schema evolution and type sharing
 type MetaContext struct {
-       typeMap               map[reflect.Type]uint32 // For writing: tracks 
written types
-       readTypeInfos         []*TypeInfo             // For reading: types 
read inline
+       typeMap               map[uintptr]uint32 // For writing: tracks written 
types
+       readTypeInfos         []*TypeInfo        // For reading: types read 
inline
        scopedMetaShareEnable bool
+       firstTypePtr          uintptr
+       hasFirstType          bool
+       typeMapActive         bool
 }
 
 // IsScopedMetaShareEnabled returns whether scoped meta share is enabled
@@ -2353,6 +2412,10 @@ func (m *MetaContext) IsScopedMetaShareEnabled() bool {
 
 // Reset clears the meta context for reuse
 func (m *MetaContext) Reset() {
-       m.typeMap = make(map[reflect.Type]uint32)
-       m.readTypeInfos = nil
+       m.hasFirstType = false
+       m.typeMapActive = false
+       m.firstTypePtr = 0
+       if m.readTypeInfos != nil {
+               m.readTypeInfos = m.readTypeInfos[:0]
+       }
 }
diff --git a/rust/fory-core/src/error.rs b/rust/fory-core/src/error.rs
index d4c5ec488..10455ee79 100644
--- a/rust/fory-core/src/error.rs
+++ b/rust/fory-core/src/error.rs
@@ -33,8 +33,21 @@ use crate::types::format_type_id;
 use thiserror::Error;
 
 /// Global flag to check if FORY_PANIC_ON_ERROR environment variable is set at 
compile time.
-/// Set FORY_PANIC_ON_ERROR=1 at compile time to enable panic on error.
-pub const PANIC_ON_ERROR: bool = option_env!("FORY_PANIC_ON_ERROR").is_some();
+/// Set FORY_PANIC_ON_ERROR=1 (or true) at compile time to enable panic on 
error.
+const fn is_truthy_env(value: &str) -> bool {
+    let bytes = value.as_bytes();
+    (bytes.len() == 1 && bytes[0] == b'1')
+        || (bytes.len() == 4
+            && (bytes[0] | 0x20) == b't'
+            && (bytes[1] | 0x20) == b'r'
+            && (bytes[2] | 0x20) == b'u'
+            && (bytes[3] | 0x20) == b'e')
+}
+
+pub const PANIC_ON_ERROR: bool = match option_env!("FORY_PANIC_ON_ERROR") {
+    Some(value) => is_truthy_env(value),
+    None => false,
+};
 
 /// Check if FORY_PANIC_ON_ERROR environment variable is set.
 #[inline(always)]
diff --git a/rust/fory-core/src/meta/type_meta.rs 
b/rust/fory-core/src/meta/type_meta.rs
index e67d76cb2..771fd2064 100644
--- a/rust/fory-core/src/meta/type_meta.rs
+++ b/rust/fory-core/src/meta/type_meta.rs
@@ -373,6 +373,52 @@ impl FieldInfo {
     }
 }
 
+const FNV_OFFSET_BASIS: u64 = 14695981039346656037;
+const FNV_PRIME: u64 = 1099511628211;
+
+#[inline(always)]
+fn fnv1a_hash_bytes(mut hash: u64, bytes: &[u8]) -> u64 {
+    for &b in bytes {
+        hash ^= b as u64;
+        hash = hash.wrapping_mul(FNV_PRIME);
+    }
+    hash
+}
+
+#[inline(always)]
+fn fnv1a_hash_u8(hash: u64, value: u8) -> u64 {
+    fnv1a_hash_bytes(hash, &[value])
+}
+
+#[inline(always)]
+fn fnv1a_hash_u32(hash: u64, value: u32) -> u64 {
+    fnv1a_hash_bytes(hash, &value.to_le_bytes())
+}
+
+#[inline(always)]
+fn hash_field_type(mut hash: u64, field_type: &FieldType) -> u64 {
+    let type_id = normalize_type_id_for_eq(field_type.type_id);
+    hash = fnv1a_hash_u32(hash, type_id);
+    hash = fnv1a_hash_u32(hash, field_type.user_type_id);
+    hash = fnv1a_hash_u8(hash, field_type.nullable as u8);
+    hash = fnv1a_hash_u8(hash, field_type.track_ref as u8);
+    hash = fnv1a_hash_u32(hash, field_type.generics.len() as u32);
+    for generic in &field_type.generics {
+        hash = hash_field_type(hash, generic);
+    }
+    hash
+}
+
+#[inline(always)]
+fn compute_schema_hash(field_infos: &[FieldInfo]) -> i64 {
+    let mut hash = fnv1a_hash_u32(FNV_OFFSET_BASIS, field_infos.len() as u32);
+    for field in field_infos {
+        hash = fnv1a_hash_bytes(hash, field.field_name.as_bytes());
+        hash = hash_field_type(hash, &field.field_type);
+    }
+    hash as i64
+}
+
 /// Sorts field infos according to the provided sorted field names and assigns 
field IDs.
 ///
 /// This function takes a vector of field infos and a slice of sorted field 
names,
@@ -436,6 +482,7 @@ impl PartialEq for FieldType {
 pub struct TypeMeta {
     // assigned valid value and used, only during deserializing
     hash: i64,
+    schema_hash: i64,
     type_id: u32,
     user_type_id: u32,
     namespace: Rc<MetaString>,
@@ -454,8 +501,10 @@ impl TypeMeta {
         register_by_name: bool,
         field_infos: Vec<FieldInfo>,
     ) -> Result<TypeMeta, Error> {
+        let schema_hash = compute_schema_hash(&field_infos);
         let mut meta = TypeMeta {
             hash: 0,
+            schema_hash,
             type_id,
             user_type_id,
             namespace: Rc::from(namespace),
@@ -490,6 +539,11 @@ impl TypeMeta {
         self.hash
     }
 
+    #[inline(always)]
+    pub fn get_schema_hash(&self) -> i64 {
+        self.schema_hash
+    }
+
     #[inline(always)]
     pub fn get_type_name(&self) -> Rc<MetaString> {
         self.type_name.clone()
@@ -509,6 +563,7 @@ impl TypeMeta {
     pub fn empty() -> Result<TypeMeta, Error> {
         Ok(TypeMeta {
             hash: 0,
+            schema_hash: 0,
             type_id: 0,
             user_type_id: NO_USER_TYPE_ID,
             namespace: Rc::from(MetaString::get_empty().clone()),
@@ -524,6 +579,7 @@ impl TypeMeta {
     pub fn deep_clone(&self) -> TypeMeta {
         TypeMeta {
             hash: self.hash,
+            schema_hash: self.schema_hash,
             type_id: self.type_id,
             user_type_id: self.user_type_id,
             namespace: Rc::new((*self.namespace).clone()),
diff --git a/rust/fory-core/src/resolver/meta_resolver.rs 
b/rust/fory-core/src/resolver/meta_resolver.rs
index 2022233b9..1f1af26c8 100644
--- a/rust/fory-core/src/resolver/meta_resolver.rs
+++ b/rust/fory-core/src/resolver/meta_resolver.rs
@@ -77,6 +77,8 @@ impl MetaWriterResolver {
 pub struct MetaReaderResolver {
     pub reading_type_infos: Vec<Rc<TypeInfo>>,
     parsed_type_infos: HashMap<i64, Rc<TypeInfo>>,
+    last_meta_header: i64,
+    last_type_info: Option<Rc<TypeInfo>>,
 }
 
 impl MetaReaderResolver {
@@ -105,7 +107,18 @@ impl MetaReaderResolver {
         } else {
             // New type - read TypeMeta inline
             let meta_header = reader.read_i64()?;
+            if let Some(type_info) = self
+                .last_type_info
+                .as_ref()
+                .filter(|_| self.last_meta_header == meta_header)
+            {
+                self.reading_type_infos.push(type_info.clone());
+                TypeMeta::skip_bytes(reader, meta_header)?;
+                return Ok(type_info.clone());
+            }
             if let Some(type_info) = self.parsed_type_infos.get(&meta_header) {
+                self.last_meta_header = meta_header;
+                self.last_type_info = Some(type_info.clone());
                 self.reading_type_infos.push(type_info.clone());
                 TypeMeta::skip_bytes(reader, meta_header)?;
                 Ok(type_info.clone())
@@ -190,6 +203,8 @@ impl MetaReaderResolver {
                     self.parsed_type_infos
                         .insert(meta_header, type_info.clone());
                 }
+                self.last_meta_header = meta_header;
+                self.last_type_info = Some(type_info.clone());
                 self.reading_type_infos.push(type_info.clone());
                 Ok(type_info)
             }
diff --git a/rust/fory-core/src/resolver/type_resolver.rs 
b/rust/fory-core/src/resolver/type_resolver.rs
index df7c8f2fa..c88f2fa61 100644
--- a/rust/fory-core/src/resolver/type_resolver.rs
+++ b/rust/fory-core/src/resolver/type_resolver.rs
@@ -234,6 +234,11 @@ impl TypeInfo {
         self.type_meta.clone()
     }
 
+    #[inline(always)]
+    pub fn get_type_meta_ref(&self) -> &TypeMeta {
+        self.type_meta.as_ref()
+    }
+
     #[inline(always)]
     pub fn is_registered_by_name(&self) -> bool {
         self.register_by_name
@@ -495,6 +500,12 @@ pub struct TypeResolver {
     partial_type_infos: HashMap<std::any::TypeId, TypeInfo>,
     // Fast lookup by numeric ID for common types
     type_id_index: Vec<TypeId>,
+    // Fast lookup by type index for user type IDs
+    user_type_id_index: Vec<u32>,
+    // Mapping from type index to Rust TypeId for fast meta lookup
+    rust_type_id_by_index: Vec<Option<std::any::TypeId>>,
+    // Fast lookup by type index for TypeMeta
+    type_meta_by_index: Vec<Option<Rc<crate::meta::TypeMeta>>>,
     compatible: bool,
     xlang: bool,
 }
@@ -516,6 +527,9 @@ impl Default for TypeResolver {
             type_info_map_by_name: HashMap::new(),
             type_info_map_by_meta_string_name: HashMap::new(),
             type_id_index: Vec::new(),
+            user_type_id_index: Vec::new(),
+            rust_type_id_by_index: Vec::new(),
+            type_meta_by_index: Vec::new(),
             partial_type_infos: HashMap::new(),
             compatible: false,
             xlang: false,
@@ -588,6 +602,80 @@ impl TypeResolver {
         )))
     }
 
+    /// Fast path for getting type info by type index (avoids HashMap lookup 
and TypeId::of)
+    #[inline(always)]
+    pub fn get_type_id_by_index(&self, index: u32) -> Result<TypeId, Error> {
+        let id_usize = index as usize;
+        if id_usize < self.type_id_index.len() {
+            let type_id_value = self.type_id_index[id_usize];
+            if type_id_value != NO_TYPE_ID {
+                return Ok(type_id_value);
+            }
+        }
+        Err(Error::type_error(format!(
+            "Type index {:?} not found in type_id_index, maybe you forgot to 
register some types",
+            index
+        )))
+    }
+
+    /// Fast path for getting user type ID by type index (avoids HashMap 
lookup by TypeId)
+    #[inline(always)]
+    pub fn get_user_type_id_by_index(
+        &self,
+        type_id: &std::any::TypeId,
+        id: u32,
+    ) -> Result<u32, Error> {
+        let id_usize = id as usize;
+        if id_usize < self.user_type_id_index.len() {
+            let user_type_id = self.user_type_id_index[id_usize];
+            if user_type_id != NO_USER_TYPE_ID {
+                return Ok(user_type_id);
+            }
+        }
+        Err(Error::type_error(format!(
+            "TypeId {:?} not found in user_type_id_index, maybe you forgot to 
register some types",
+            type_id
+        )))
+    }
+
+    /// Fast path for getting TypeMeta by type index (avoids HashMap lookup by 
TypeId)
+    #[inline(always)]
+    pub fn get_type_meta_by_index(
+        &self,
+        type_id: &std::any::TypeId,
+        index: u32,
+    ) -> Result<Rc<crate::meta::TypeMeta>, Error> {
+        let id_usize = index as usize;
+        if id_usize < self.type_meta_by_index.len() {
+            if let Some(meta) = &self.type_meta_by_index[id_usize] {
+                return Ok(meta.clone());
+            }
+        }
+        Err(Error::type_error(format!(
+            "TypeId {:?} not found in type_meta_by_index, maybe you forgot to 
register some types",
+            type_id
+        )))
+    }
+
+    /// Fast path for getting TypeMeta by type index without cloning Rc.
+    #[inline(always)]
+    pub fn get_type_meta_by_index_ref(
+        &self,
+        type_id: &std::any::TypeId,
+        index: u32,
+    ) -> Result<&crate::meta::TypeMeta, Error> {
+        let id_usize = index as usize;
+        if id_usize < self.type_meta_by_index.len() {
+            if let Some(meta) = &self.type_meta_by_index[id_usize] {
+                return Ok(meta.as_ref());
+            }
+        }
+        Err(Error::type_error(format!(
+            "TypeId {:?} not found in type_meta_by_index, maybe you forgot to 
register some types",
+            type_id
+        )))
+    }
+
     #[inline(always)]
     pub fn get_harness(&self, id: u32) -> Option<Rc<Harness>> {
         self.get_type_info_by_id(id)
@@ -856,10 +944,12 @@ impl TypeResolver {
             )));
         }
 
-        // Update type_id_index for fast lookup
+        // Update type_id_index/user_type_id_index for fast lookup
         let index = T::fory_type_index() as usize;
         if index >= self.type_id_index.len() {
             self.type_id_index.resize(index + 1, NO_TYPE_ID);
+            self.user_type_id_index.resize(index + 1, NO_USER_TYPE_ID);
+            self.rust_type_id_by_index.resize(index + 1, None);
         } else if self.type_id_index[index] != NO_TYPE_ID {
             return Err(Error::type_error(format!(
                 "Type index {:?} already registered",
@@ -867,6 +957,8 @@ impl TypeResolver {
             )));
         }
         self.type_id_index[index] = type_info.type_id;
+        self.user_type_id_index[index] = type_info.user_type_id;
+        self.rust_type_id_by_index[index] = Some(rs_type_id);
 
         // Insert partial type info into id maps
         if crate::types::is_internal_type(actual_type_id) {
@@ -1147,6 +1239,8 @@ impl TypeResolver {
         let mut type_info_map_by_name = self.type_info_map_by_name.clone();
         let mut type_info_map_by_meta_string_name = 
self.type_info_map_by_meta_string_name.clone();
         let type_id_index = self.type_id_index.clone();
+        let rust_type_id_by_index = self.rust_type_id_by_index.clone();
+        let user_type_id_index = self.user_type_id_index.clone();
 
         // Iterate over partial_type_infos and complete them
         for (_rust_type_id, partial_type_info) in 
self.partial_type_infos.iter() {
@@ -1181,6 +1275,13 @@ impl TypeResolver {
             }
         }
 
+        let type_meta_by_index: Vec<Option<Rc<crate::meta::TypeMeta>>> = 
rust_type_id_by_index
+            .iter()
+            .map(|id| {
+                id.and_then(|rust_id| type_info_map.get(&rust_id).map(|info| 
info.get_type_meta()))
+            })
+            .collect();
+
         Ok(TypeResolver {
             internal_type_info_by_id,
             user_type_info_by_id,
@@ -1189,6 +1290,9 @@ impl TypeResolver {
             type_info_map_by_meta_string_name,
             partial_type_infos: HashMap::new(),
             type_id_index,
+            user_type_id_index,
+            rust_type_id_by_index,
+            type_meta_by_index,
             compatible: self.compatible,
             xlang: self.xlang,
         })
@@ -1278,6 +1382,9 @@ impl TypeResolver {
             type_info_map_by_meta_string_name,
             partial_type_infos: HashMap::new(),
             type_id_index: self.type_id_index.clone(),
+            user_type_id_index: self.user_type_id_index.clone(),
+            rust_type_id_by_index: self.rust_type_id_by_index.clone(),
+            type_meta_by_index: self.type_meta_by_index.clone(),
             compatible: self.compatible,
             xlang: self.xlang,
         }
diff --git a/rust/fory-core/src/serializer/collection.rs 
b/rust/fory-core/src/serializer/collection.rs
index 71985a977..68a6dc6a4 100644
--- a/rust/fory-core/src/serializer/collection.rs
+++ b/rust/fory-core/src/serializer/collection.rs
@@ -262,6 +262,97 @@ where
     }
 }
 
+#[inline(always)]
+pub fn read_vec_data<T>(context: &mut ReadContext) -> Result<Vec<T>, Error>
+where
+    T: Serializer + ForyDefault,
+{
+    let len = context.reader.read_varuint32()?;
+    if len == 0 {
+        return Ok(Vec::new());
+    }
+    if T::fory_is_polymorphic() || T::fory_is_shared_ref() {
+        return read_vec_data_dyn_ref(context, len);
+    }
+    let header = context.reader.read_u8()?;
+    let declared = (header & DECL_ELEMENT_TYPE) != 0;
+    if !declared {
+        T::fory_read_type_info(context)?;
+    }
+    let has_null = (header & HAS_NULL) != 0;
+    ensure!(
+        (header & IS_SAME_TYPE) != 0,
+        Error::type_error("Type inconsistent, target type is not polymorphic")
+    );
+    let mut vec = Vec::with_capacity(len as usize);
+    if !has_null {
+        for _ in 0..len {
+            vec.push(T::fory_read_data(context)?);
+        }
+    } else {
+        for _ in 0..len {
+            let flag = context.reader.read_i8()?;
+            if flag == RefFlag::Null as i8 {
+                vec.push(T::fory_default());
+            } else {
+                vec.push(T::fory_read_data(context)?);
+            }
+        }
+    }
+    Ok(vec)
+}
+
+#[inline(always)]
+fn read_vec_data_dyn_ref<T>(context: &mut ReadContext, len: u32) -> 
Result<Vec<T>, Error>
+where
+    T: Serializer + ForyDefault,
+{
+    let header = context.reader.read_u8()?;
+    let is_track_ref = (header & TRACKING_REF) != 0;
+    let is_same_type = (header & IS_SAME_TYPE) != 0;
+    let has_null = (header & HAS_NULL) != 0;
+    let is_declared = (header & DECL_ELEMENT_TYPE) != 0;
+
+    let elem_ref_mode = if is_track_ref {
+        RefMode::Tracking
+    } else if has_null {
+        RefMode::NullOnly
+    } else {
+        RefMode::None
+    };
+
+    let mut vec = Vec::with_capacity(len as usize);
+    if is_same_type {
+        let type_info = if !is_declared {
+            context.read_any_type_info()?
+        } else {
+            T::fory_get_type_info(context.get_type_resolver())?
+        };
+        if elem_ref_mode == RefMode::None {
+            for _ in 0..len {
+                vec.push(T::fory_read_with_type_info(
+                    context,
+                    RefMode::None,
+                    type_info.clone(),
+                )?);
+            }
+        } else {
+            for _ in 0..len {
+                vec.push(T::fory_read_with_type_info(
+                    context,
+                    elem_ref_mode,
+                    type_info.clone(),
+                )?);
+            }
+        }
+    } else {
+        for _ in 0..len {
+            vec.push(T::fory_read(context, elem_ref_mode, true)?);
+        }
+    }
+    Ok(vec)
+}
+
 /// Slow but versatile collection deserialization for dynamic trait object and 
shared/circular reference.
 pub fn read_collection_data_dyn_ref<C, T>(context: &mut ReadContext, len: u32) 
-> Result<C, Error>
 where
diff --git a/rust/fory-core/src/serializer/list.rs 
b/rust/fory-core/src/serializer/list.rs
index 725471157..54fad4f61 100644
--- a/rust/fory-core/src/serializer/list.rs
+++ b/rust/fory-core/src/serializer/list.rs
@@ -26,7 +26,7 @@ use std::collections::{LinkedList, VecDeque};
 use std::mem;
 
 use super::collection::{
-    read_collection_data, read_collection_type_info, write_collection_data,
+    read_collection_data, read_collection_type_info, read_vec_data, 
write_collection_data,
     write_collection_type_info,
 };
 
@@ -126,7 +126,7 @@ impl<T: Serializer + ForyDefault> Serializer for Vec<T> {
         if is_primitive_type::<T>() {
             primitive_list::fory_read_data(context)
         } else {
-            read_collection_data(context)
+            read_vec_data(context)
         }
     }
 
diff --git a/rust/fory-core/src/serializer/struct_.rs 
b/rust/fory-core/src/serializer/struct_.rs
index 51dd6a0f0..aecc8711b 100644
--- a/rust/fory-core/src/serializer/struct_.rs
+++ b/rust/fory-core/src/serializer/struct_.rs
@@ -15,9 +15,10 @@
 // specific language governing permissions and limitations
 // under the License.
 
+use crate::ensure;
 use crate::error::Error;
 use crate::resolver::context::{ReadContext, WriteContext};
-use crate::serializer::Serializer;
+use crate::serializer::{Serializer, StructSerializer};
 use crate::types::{RefFlag, RefMode, TypeId};
 use crate::util::ENABLE_FORY_DEBUG_OUTPUT;
 use std::any::Any;
@@ -51,6 +52,36 @@ pub fn read_type_info<T: Serializer>(context: &mut 
ReadContext) -> Result<(), Er
     Ok(())
 }
 
+#[inline(always)]
+pub fn read_type_info_fast<T: StructSerializer>(context: &mut ReadContext) -> 
Result<(), Error> {
+    if context.is_compatible() || context.is_xlang() {
+        return read_type_info::<T>(context);
+    }
+    let local_type_id = context
+        .get_type_resolver()
+        .get_type_id_by_index(T::fory_type_index())?;
+    let local_type_id_u32 = local_type_id as u32;
+    if !crate::types::needs_user_type_id(local_type_id_u32) {
+        return read_type_info::<T>(context);
+    }
+    let remote_type_id = context.reader.read_u8()? as u32;
+    ensure!(
+        local_type_id_u32 == remote_type_id,
+        Error::type_mismatch(local_type_id_u32, remote_type_id)
+    );
+    let remote_user_type_id = context.reader.read_varuint32()?;
+    let local_user_type_id = context
+        .get_type_resolver()
+        .get_user_type_id_by_index(&std::any::TypeId::of::<T>(), 
T::fory_type_index())?;
+    if remote_user_type_id != local_user_type_id {
+        return Err(Error::type_error(format!(
+            "User type id mismatch: local {} vs remote {}",
+            local_user_type_id, remote_user_type_id
+        )));
+    }
+    Ok(())
+}
+
 #[inline(always)]
 pub fn write<T: Serializer>(
     this: &T,
diff --git a/rust/fory-derive/src/object/read.rs 
b/rust/fory-derive/src/object/read.rs
index ba1e7aa44..4f931bd49 100644
--- a/rust/fory-derive/src/object/read.rs
+++ b/rust/fory-derive/src/object/read.rs
@@ -441,7 +441,7 @@ pub fn gen_read_field(field: &Field, private_ident: &Ident, 
field_name: &str) ->
 
 pub fn gen_read_type_info() -> TokenStream {
     quote! {
-        fory_core::serializer::struct_::read_type_info::<Self>(context)
+        fory_core::serializer::struct_::read_type_info_fast::<Self>(context)
     }
 }
 
@@ -1068,23 +1068,36 @@ pub(crate) fn gen_read_compatible_with_construction(
         quote! {}
     };
 
+    let fields_binding = if variant_ident.is_some() {
+        quote! {
+            let mut fields = remote_meta.get_field_infos().clone();
+            #variant_field_remap
+        }
+    } else {
+        quote! {
+            let fields = remote_meta.get_field_infos();
+        }
+    };
+
     quote! {
-        let mut fields = type_info.get_type_meta().get_field_infos().clone();
-        #variant_field_remap
-        #(#declare_ts)*
-        let meta = 
context.get_type_info(&std::any::TypeId::of::<Self>())?.get_type_meta();
+        let meta = context.get_type_resolver().get_type_meta_by_index_ref(
+            &std::any::TypeId::of::<Self>(),
+            <Self as fory_core::StructSerializer>::fory_type_index(),
+        )?;
         let local_type_hash = meta.get_hash();
-        let remote_type_hash = type_info.get_type_meta().get_hash();
+        let remote_meta = type_info.get_type_meta_ref();
+        let remote_type_hash = remote_meta.get_hash();
         if remote_type_hash == local_type_hash {
-            <Self as fory_core::Serializer>::fory_read_data(context)
-        } else {
-            for _field in fields.iter() {
-                match _field.field_id {
-                    #(#match_arms)*
-                    #skip_arm
-                }
+            return <Self as fory_core::Serializer>::fory_read_data(context);
+        }
+        #fields_binding
+        #(#declare_ts)*
+        for _field in fields.iter() {
+            match _field.field_id {
+                #(#match_arms)*
+                #skip_arm
             }
-            #construction
         }
+        #construction
     }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to