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 c727ea5a2 perf(java): optimize json serialize perf (#3834)
c727ea5a2 is described below

commit c727ea5a26e707e2cbfcb8604112323702ed0246
Author: Shawn Yang <[email protected]>
AuthorDate: Sun Jul 12 19:51:49 2026 +0530

    perf(java): optimize json serialize perf (#3834)
    
    ## Why?
    
    
    
    ## What does this PR do?
    
    
    
    ## Related issues
    
    
    
    ## AI Contribution Checklist
    
    
    
    - [ ] Substantial AI assistance was used in this PR: `yes` / `no`
    - [ ] If `yes`, I included a completed [AI Contribution
    
Checklist](https://github.com/apache/fory/blob/main/AI_POLICY.md#9-contributor-checklist-for-ai-assisted-prs)
    in this PR description and the required `AI Usage Disclosure`.
    - [ ] If `yes`, my PR description includes the required `ai_review`
    summary and screenshot evidence or equivalent persisted links of the
    final clean AI review results from both fresh reviewers described in
    `AI_POLICY.md`, the Fory-guided reviewer and the independent general
    reviewer, on the current PR diff or current HEAD after the latest code
    changes.
    
    
    
    ## 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                                         |   1 +
 .../json/codegen/JsonGeneratedCodecBuilder.java    |   5 +
 .../fory/json/codegen/JsonReaderCodegen.java       |  23 ++-
 .../fory/json/codegen/JsonWriterCodegen.java       | 218 ++++++++++++++++-----
 .../org/apache/fory/json/meta/JsonFieldInfo.java   |  32 ++-
 .../apache/fory/json/writer/StringJsonWriter.java  |  67 ++++---
 .../apache/fory/json/writer/Utf8JsonWriter.java    |  73 +++++--
 7 files changed, 315 insertions(+), 104 deletions(-)

diff --git a/.gitignore b/.gitignore
index ba7a5f9ff..b93f7801d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -110,6 +110,7 @@ examples/cpp/cmake_example/build
 **/benchmark_results.json
 **/benchmark_report.md
 **/benchmark_*.png
+.cli.params.json
 benchmarks/java/reports/
 **/results/
 benchmarks/**/report/
diff --git 
a/java/fory-json/src/main/java/org/apache/fory/json/codegen/JsonGeneratedCodecBuilder.java
 
b/java/fory-json/src/main/java/org/apache/fory/json/codegen/JsonGeneratedCodecBuilder.java
index dfb2ec20a..3601349a8 100644
--- 
a/java/fory-json/src/main/java/org/apache/fory/json/codegen/JsonGeneratedCodecBuilder.java
+++ 
b/java/fory-json/src/main/java/org/apache/fory/json/codegen/JsonGeneratedCodecBuilder.java
@@ -19,6 +19,8 @@
 
 package org.apache.fory.json.codegen;
 
+import static org.apache.fory.codegen.ExpressionUtils.inline;
+
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import org.apache.fory.builder.CodecBuilder;
@@ -123,6 +125,9 @@ final class JsonGeneratedCodecBuilder extends CodecBuilder {
   }
 
   Expression setField(JsonFieldInfo property, Expression object, Expression 
value) {
+    // A parsed member value has one store owner. Keep that expression inline 
so generated readers
+    // do not add a local store/load pair before the setter, field, or 
VarHandle write.
+    value = inline(value);
     Method setter = property.readSetter();
     if (setter != null) {
       Class<?> rawType = setter.getParameterTypes()[0];
diff --git 
a/java/fory-json/src/main/java/org/apache/fory/json/codegen/JsonReaderCodegen.java
 
b/java/fory-json/src/main/java/org/apache/fory/json/codegen/JsonReaderCodegen.java
index f9887019a..e7e01d0c2 100644
--- 
a/java/fory-json/src/main/java/org/apache/fory/json/codegen/JsonReaderCodegen.java
+++ 
b/java/fory-json/src/main/java/org/apache/fory/json/codegen/JsonReaderCodegen.java
@@ -303,7 +303,7 @@ abstract class JsonReaderCodegen {
     expressions.add(
         new Expression.Assign(
             new Reference("this.readTable", TypeRef.of(JsonFieldTable.class)),
-            new Expression.Invoke(owner, "readTable", 
TypeRef.of(JsonFieldTable.class))));
+            new Expression.Invoke(owner, "readTable", 
TypeRef.of(JsonFieldTable.class)).inline()));
     if (record) {
       expressions.add(
           new Expression.Assign(new Reference("this.owner", 
TypeRef.of(ObjectCodec.class)), owner));
@@ -368,9 +368,11 @@ abstract class JsonReaderCodegen {
       expressions.add(returnObject(object, record));
       return expressions;
     }
-    Expression hashes =
-        new Expression.Variable("localFieldHashes", fieldRef("fieldHashes", 
long[].class));
-    expressions.add(hashes);
+    Expression hashes = fieldRef("fieldHashes", long[].class);
+    if (properties.length > 1) {
+      hashes = new Expression.Variable("localFieldHashes", hashes);
+      expressions.add(hashes);
+    }
     Expression[] skips = new Expression[properties.length];
     for (int i = 1; i < properties.length; i++) {
       skips[i] = new Expression.Variable("skip" + i, Expression.Literal.False);
@@ -732,10 +734,11 @@ abstract class JsonReaderCodegen {
     if (record) {
       return new Expression.Variable(
           "object",
-          new Expression.Invoke(
-              ownerRef(), "newRecordFieldValues", TypeRef.of(Object[].class), 
false));
+          inline(
+              new Expression.Invoke(
+                  ownerRef(), "newRecordFieldValues", 
TypeRef.of(Object[].class), false)));
     }
-    return new Expression.Variable("object", builder.newObject());
+    return builder.newObject();
   }
 
   private Expression returnObject(Expression object, boolean record) {
@@ -744,7 +747,9 @@ abstract class JsonReaderCodegen {
       return new Expression.ListExpression(
           exitDepth,
           new Expression.Return(
-              new Expression.Invoke(ownerRef(), "newRecord", 
TypeRef.of(Object.class), object)));
+              inline(
+                  new Expression.Invoke(
+                      ownerRef(), "newRecord", TypeRef.of(Object.class), 
object))));
     }
     return new Expression.ListExpression(exitDepth, new 
Expression.Return(object));
   }
@@ -1651,7 +1656,7 @@ abstract class JsonReaderCodegen {
   }
 
   final Expression assignRecord(Expression object, int id, Expression value) {
-    return new Expression.AssignArrayElem(object, value, 
Expression.Literal.ofInt(id));
+    return new Expression.AssignArrayElem(object, inline(value), 
Expression.Literal.ofInt(id));
   }
 
   final Expression box(Class<?> boxedType, Expression value) {
diff --git 
a/java/fory-json/src/main/java/org/apache/fory/json/codegen/JsonWriterCodegen.java
 
b/java/fory-json/src/main/java/org/apache/fory/json/codegen/JsonWriterCodegen.java
index faa53a1ed..54078a40e 100644
--- 
a/java/fory-json/src/main/java/org/apache/fory/json/codegen/JsonWriterCodegen.java
+++ 
b/java/fory-json/src/main/java/org/apache/fory/json/codegen/JsonWriterCodegen.java
@@ -23,7 +23,6 @@ import static org.apache.fory.codegen.ExpressionUtils.add;
 import static org.apache.fory.codegen.ExpressionUtils.cast;
 import static org.apache.fory.codegen.ExpressionUtils.eq;
 import static org.apache.fory.codegen.ExpressionUtils.inline;
-import static org.apache.fory.codegen.ExpressionUtils.valueOf;
 
 import java.math.BigDecimal;
 import java.time.LocalDate;
@@ -88,23 +87,26 @@ abstract class JsonWriterCodegen {
 
   abstract int splitMemberThreshold();
 
-  abstract StringPrefixFields prefixFields(JsonFieldInfo[] properties, boolean 
objectStartFused);
+  abstract PrefixFields prefixFields(JsonFieldInfo[] properties, boolean 
objectStartFused);
 
   abstract void addPrefixFields(
-      CodegenContext ctx, JsonFieldInfo property, int id, StringPrefixFields 
fields);
+      CodegenContext ctx, JsonFieldInfo property, int id, PrefixFields fields);
 
   abstract void addPrefixAssignments(
       Expression.ListExpression expressions,
       Expression property,
       JsonFieldInfo field,
       int id,
-      StringPrefixFields fields);
+      PrefixFields fields);
 
   abstract Reference writerRef();
 
   abstract Expression writeObjectStartPrimitive(
       JsonFieldInfo property, Expression value, Expression writer);
 
+  abstract Expression tryWriteObjectStartString(
+      JsonFieldInfo property, Expression value, Expression writer);
+
   abstract Expression writeNumberField(
       JsonFieldInfo property,
       int id,
@@ -184,7 +186,7 @@ abstract class JsonWriterCodegen {
     ctx.addImports(writerType());
     ctx.implementsInterfaces(JsonCodegen.generatedCodecType(ctx, 
objectWriterType()));
     boolean objectStartFused = canFuseObjectStart(properties);
-    StringPrefixFields prefixFields = prefixFields(properties, 
objectStartFused);
+    PrefixFields prefixFields = prefixFields(properties, objectStartFused);
     for (int i = 0; i < properties.length; i++) {
       JsonFieldInfo property = properties[i];
       if (usesWriteInfo(property)) {
@@ -223,11 +225,12 @@ abstract class JsonWriterCodegen {
       bodyCode = "this." + objectMethod + "(writer, (" + ctx.type(type) + ") 
value);\n";
     } else {
       ctx.clearExprState();
-      Expression object =
-          new Expression.Variable(
-              "object",
+      Expression castObject =
+          inline(
               new Expression.Cast(
                   new Reference("value", TypeRef.of(Object.class)), 
TypeRef.of(type)));
+      Expression object =
+          properties.length <= 1 ? castObject : new 
Expression.Variable("object", castObject);
       Code.ExprCode body =
           writeExpression(builder, properties, objectStartFused, 
object).genCode(ctx);
       bodyCode = body.code();
@@ -245,9 +248,8 @@ abstract class JsonWriterCodegen {
     return ctx.genCode();
   }
 
-  final StringPrefixFields stringPrefixFields(
-      JsonFieldInfo[] properties, boolean objectStartFused) {
-    StringPrefixFields fields = new StringPrefixFields(properties.length);
+  final PrefixFields stringPrefixFields(JsonFieldInfo[] properties, boolean 
objectStartFused) {
+    PrefixFields fields = new PrefixFields(properties.length);
     boolean commaKnown = objectStartFused;
     for (int i = 0; i < properties.length; i++) {
       JsonFieldInfo property = properties[i];
@@ -268,7 +270,7 @@ abstract class JsonWriterCodegen {
   }
 
   private static void markStringUtf16PrefixField(
-      JsonFieldInfo property, boolean commaKnown, StringPrefixFields fields, 
int id) {
+      JsonFieldInfo property, boolean commaKnown, PrefixFields fields, int id) 
{
     if (!commaKnown) {
       fields.name[id] = true;
       fields.comma[id] = true;
@@ -279,11 +281,60 @@ abstract class JsonWriterCodegen {
     }
   }
 
-  private static final class StringPrefixFields {
+  final PrefixFields utf8PrefixFields(JsonFieldInfo[] properties, boolean 
objectStartFused) {
+    PrefixFields fields = new PrefixFields(properties.length);
+    boolean commaKnown = objectStartFused;
+    for (int i = 0; i < properties.length; i++) {
+      JsonFieldInfo property = properties[i];
+      if (usesPrefix(property)) {
+        if (i == 0
+            && !objectStartFused
+            && !writeNullFields
+            && Utf8Generator.canPackObjectStartString(property)) {
+          // The generated first-field branch consumes neither ordinary prefix 
field.
+        } else if (objectStartFused && i == 0) {
+          if (!canPackPrefix(property, false)) {
+            fields.name[i] = true;
+          }
+        } else if (!commaKnown) {
+          if (!canUsePackedDynamicPrefix(property)
+              || !canPackSinglePrefix(property, false)
+              || !canPackSinglePrefix(property, true)) {
+            fields.name[i] = true;
+            fields.comma[i] = true;
+          }
+        } else if (!canPackPrefix(property, true)) {
+          fields.comma[i] = true;
+        }
+      }
+      if (writeNullFields || property.writeRawType().isPrimitive()) {
+        commaKnown = true;
+      }
+    }
+    return fields;
+  }
+
+  private boolean canUsePackedDynamicPrefix(JsonFieldInfo property) {
+    if (writeNullFields && !property.writeRawType().isPrimitive()) {
+      return false;
+    }
+    switch (property.writeKind()) {
+      case BYTE:
+      case SHORT:
+      case INT:
+      case LONG:
+      case STRING:
+        return true;
+      default:
+        return false;
+    }
+  }
+
+  private static final class PrefixFields {
     private final boolean[] name;
     private final boolean[] comma;
 
-    private StringPrefixFields(int size) {
+    private PrefixFields(int size) {
       name = new boolean[size];
       comma = new boolean[size];
     }
@@ -301,7 +352,7 @@ abstract class JsonWriterCodegen {
   }
 
   private Expression writerConstructorExpression(
-      JsonFieldInfo[] properties, StringPrefixFields prefixFields) {
+      JsonFieldInfo[] properties, PrefixFields prefixFields) {
     Expression.ListExpression expressions = new Expression.ListExpression();
     Reference propertiesRef = new Reference("properties", 
TypeRef.of(JsonFieldInfo[].class));
     Reference codecsRef = new Reference("codecs", 
TypeRef.of(codecArrayType()));
@@ -337,20 +388,49 @@ abstract class JsonWriterCodegen {
     Expression.ListExpression expressions = new Expression.ListExpression();
     expressions.add(object);
     Expression index = null;
+    int firstProperty = 0;
     if (!objectStartFused) {
-      expressions.add(new Expression.Invoke(writer, "writeObjectStart"));
       index = new Expression.Variable("index", Expression.Literal.ofInt(0));
-      expressions.add(index);
+      JsonFieldInfo first = properties.length == 0 ? null : properties[0];
+      Expression value =
+          first != null && !writeNullFields && first.writeKind() == 
JsonFieldKind.STRING
+              ? new Expression.Variable(
+                  "v0", cast(inline(builder.fieldValue(first, object)), 
TypeRef.of(String.class)))
+              : null;
+      Expression fusedStart =
+          value == null ? null : tryWriteObjectStartString(first, value, 
writer);
+      if (fusedStart != null) {
+        expressions.add(value);
+        boolean hasRemainingProperties = properties.length > 1;
+        if (hasRemainingProperties) {
+          expressions.add(index);
+        }
+        Expression present = fusedStart;
+        if (hasRemainingProperties) {
+          present =
+              new Expression.ListExpression(
+                  fusedStart, new Expression.Assign(index, 
Expression.Literal.ofInt(1)));
+        }
+        expressions.add(
+            new Expression.If(
+                ne(value, new Expression.Null(TypeRef.of(String.class), 
false)),
+                present,
+                new Expression.Invoke(writer, "writeObjectStart")));
+        firstProperty = 1;
+      } else {
+        expressions.add(new Expression.Invoke(writer, "writeObjectStart"));
+        expressions.add(index);
+      }
     }
     boolean commaKnown = objectStartFused;
     boolean splitMembers = properties.length >= splitMemberThreshold();
     List<Expression> memberGroup = splitMembers ? new 
ArrayList<>(MAX_MEMBERS_PER_METHOD) : null;
-    for (int i = 0; i < properties.length; i++) {
+    for (int i = firstProperty; i < properties.length; i++) {
       Expression member;
       if (objectStartFused && i == 0) {
         member =
             writeObjectStartPrimitive(
-                properties[i], builder.fieldValue(properties[i], object), 
writer);
+                properties[i], inline(builder.fieldValue(properties[i], 
object)), writer);
       } else {
         member = writeProp(builder, properties[i], i, commaKnown, index, 
object, writer);
       }
@@ -440,15 +520,13 @@ abstract class JsonWriterCodegen {
       Expression writer) {
     Class<?> rawType = property.writeRawType();
     if (rawType.isPrimitive()) {
-      Expression fieldValue = builder.fieldValue(property, object);
+      // Primitive members cannot be null and this path consumes the access 
once. Nullable
+      // references stay cached below because their null check and write must 
share one value.
+      Expression fieldValue = inline(builder.fieldValue(property, object));
       if (property.writeKind() == JsonFieldKind.OBJECT) {
-        Expression value =
-            new Expression.Variable(
-                "v" + id, valueOf(TypeRef.of(rawType).wrap(), 
inline(fieldValue)));
         return new Expression.ListExpression(
-            value,
             writeFieldName(property, id, commaKnown, index, writer),
-            writeCodec(property, id, value, writer));
+            writeCodec(property, id, fieldValue, writer));
       }
       return writePrimitive(property, id, fieldValue, commaKnown, index, 
writer);
     }
@@ -624,6 +702,19 @@ abstract class JsonWriterCodegen {
     return args;
   }
 
+  private static Expression[] objectPackedPrefixArgs(JsonFieldInfo property, 
Expression value) {
+    byte[] namePrefix = property.utf8NamePrefix();
+    byte[] prefix = new byte[namePrefix.length + 1];
+    prefix[0] = '{';
+    System.arraycopy(namePrefix, 0, prefix, 1, namePrefix.length);
+    return new Expression[] {
+      Expression.Literal.ofLong(packedPrefixWord(prefix, 0)),
+      Expression.Literal.ofLong(packedPrefixWord(prefix, Long.BYTES)),
+      Expression.Literal.ofInt(prefix.length),
+      value
+    };
+  }
+
   private static Expression[] stringPackedPrefixArgs(
       JsonFieldInfo property, int id, boolean comma, Expression... extraArgs) {
     byte[] prefix =
@@ -809,13 +900,12 @@ abstract class JsonWriterCodegen {
     }
 
     @Override
-    StringPrefixFields prefixFields(JsonFieldInfo[] properties, boolean 
objectStartFused) {
+    PrefixFields prefixFields(JsonFieldInfo[] properties, boolean 
objectStartFused) {
       return stringPrefixFields(properties, objectStartFused);
     }
 
     @Override
-    void addPrefixFields(
-        CodegenContext ctx, JsonFieldInfo property, int id, StringPrefixFields 
fields) {
+    void addPrefixFields(CodegenContext ctx, JsonFieldInfo property, int id, 
PrefixFields fields) {
       ctx.addField(byte[].class, "s" + id);
       ctx.addField(byte[].class, "sc" + id);
       if (fields.name[id]) {
@@ -832,7 +922,7 @@ abstract class JsonWriterCodegen {
         Expression property,
         JsonFieldInfo field,
         int id,
-        StringPrefixFields fields) {
+        PrefixFields fields) {
       expressions.add(
           new Expression.Assign(
               stringPrefixRef(false, id),
@@ -873,10 +963,10 @@ abstract class JsonWriterCodegen {
         case BYTE:
         case SHORT:
         case INT:
-          method = "writeObjectIntField";
+          method = "writeObjectStartWithIntField";
           break;
         case LONG:
-          method = "writeObjectLongField";
+          method = "writeObjectStartWithLongField";
           break;
         default:
           throw new ForyJsonException(
@@ -890,6 +980,12 @@ abstract class JsonWriterCodegen {
           writer, method, stringPrefixRef(false, 0), utf16PrefixRef(false, 0), 
value);
     }
 
+    @Override
+    Expression tryWriteObjectStartString(
+        JsonFieldInfo property, Expression value, Expression writer) {
+      return null;
+    }
+
     @Override
     Expression writeNumberField(
         JsonFieldInfo property,
@@ -1050,15 +1146,18 @@ abstract class JsonWriterCodegen {
     }
 
     @Override
-    StringPrefixFields prefixFields(JsonFieldInfo[] properties, boolean 
objectStartFused) {
-      return null;
+    PrefixFields prefixFields(JsonFieldInfo[] properties, boolean 
objectStartFused) {
+      return utf8PrefixFields(properties, objectStartFused);
     }
 
     @Override
-    void addPrefixFields(
-        CodegenContext ctx, JsonFieldInfo property, int id, StringPrefixFields 
fields) {
-      ctx.addField(byte[].class, "u" + id);
-      ctx.addField(byte[].class, "uc" + id);
+    void addPrefixFields(CodegenContext ctx, JsonFieldInfo property, int id, 
PrefixFields fields) {
+      if (fields.name[id]) {
+        ctx.addField(byte[].class, "u" + id);
+      }
+      if (fields.comma[id]) {
+        ctx.addField(byte[].class, "uc" + id);
+      }
     }
 
     @Override
@@ -1067,17 +1166,21 @@ abstract class JsonWriterCodegen {
         Expression property,
         JsonFieldInfo field,
         int id,
-        StringPrefixFields fields) {
-      expressions.add(
-          new Expression.Assign(
-              utf8PrefixRef(false, id),
-              new Expression.Invoke(property, "utf8NamePrefix", 
TypeRef.of(byte[].class))
-                  .inline()));
-      expressions.add(
-          new Expression.Assign(
-              utf8PrefixRef(true, id),
-              new Expression.Invoke(property, "utf8CommaNamePrefix", 
TypeRef.of(byte[].class))
-                  .inline()));
+        PrefixFields fields) {
+      if (fields.name[id]) {
+        expressions.add(
+            new Expression.Assign(
+                utf8PrefixRef(false, id),
+                new Expression.Invoke(property, "utf8NamePrefix", 
TypeRef.of(byte[].class))
+                    .inline()));
+      }
+      if (fields.comma[id]) {
+        expressions.add(
+            new Expression.Assign(
+                utf8PrefixRef(true, id),
+                new Expression.Invoke(property, "utf8CommaNamePrefix", 
TypeRef.of(byte[].class))
+                    .inline()));
+      }
     }
 
     @Override
@@ -1093,10 +1196,10 @@ abstract class JsonWriterCodegen {
         case BYTE:
         case SHORT:
         case INT:
-          method = "writeObjectIntField";
+          method = "writeObjectStartWithIntField";
           break;
         case LONG:
-          method = "writeObjectLongField";
+          method = "writeObjectStartWithLongField";
           break;
         default:
           throw new ForyJsonException(
@@ -1108,6 +1211,21 @@ abstract class JsonWriterCodegen {
       return new Expression.Invoke(writer, method, utf8PrefixRef(false, 0), 
value);
     }
 
+    @Override
+    Expression tryWriteObjectStartString(
+        JsonFieldInfo property, Expression value, Expression writer) {
+      if (!canPackObjectStartString(property)) {
+        return null;
+      }
+      return new Expression.Invoke(
+          writer, "writeObjectStartWithStringField", 
objectPackedPrefixArgs(property, value));
+    }
+
+    private static boolean canPackObjectStartString(JsonFieldInfo property) {
+      return property.writeKind() == JsonFieldKind.STRING
+          && property.utf8NamePrefix().length < Long.BYTES * 2;
+    }
+
     @Override
     Expression writeNumberField(
         JsonFieldInfo property,
diff --git 
a/java/fory-json/src/main/java/org/apache/fory/json/meta/JsonFieldInfo.java 
b/java/fory-json/src/main/java/org/apache/fory/json/meta/JsonFieldInfo.java
index ecbe09468..4c1b0e7b5 100644
--- a/java/fory-json/src/main/java/org/apache/fory/json/meta/JsonFieldInfo.java
+++ b/java/fory-json/src/main/java/org/apache/fory/json/meta/JsonFieldInfo.java
@@ -97,6 +97,10 @@ public final class JsonFieldInfo {
   private final byte[] stringUtf16CommaNamePrefix;
   private final byte[] utf8NamePrefix;
   private final byte[] utf8CommaNamePrefix;
+  private final long utf8NamePrefixWord0;
+  private final long utf8NamePrefixWord1;
+  private final long utf8CommaNamePrefixWord0;
+  private final long utf8CommaNamePrefixWord1;
   private final byte[][] stringEnumValues;
   private final byte[][] stringElementEnumValues;
   private final byte[][] stringEnumNameValues;
@@ -165,6 +169,10 @@ public final class JsonFieldInfo {
     stringUtf16CommaNamePrefix = toUtf16Bytes(stringCommaNamePrefix);
     utf8NamePrefix = utf8Prefix.getBytes(StandardCharsets.UTF_8);
     utf8CommaNamePrefix = ("," + utf8Prefix).getBytes(StandardCharsets.UTF_8);
+    utf8NamePrefixWord0 = packedPrefixWord(utf8NamePrefix, 0);
+    utf8NamePrefixWord1 = packedPrefixWord(utf8NamePrefix, Long.BYTES);
+    utf8CommaNamePrefixWord0 = packedPrefixWord(utf8CommaNamePrefix, 0);
+    utf8CommaNamePrefixWord1 = packedPrefixWord(utf8CommaNamePrefix, 
Long.BYTES);
     stringEnumValues = writeKind == JsonFieldKind.ENUM ? 
stringEnumValues(writeRawType) : null;
     stringEnumNameValues =
         writeKind == JsonFieldKind.ENUM ? fieldValues(stringNamePrefix, 
stringEnumValues) : null;
@@ -994,8 +1002,21 @@ public final class JsonFieldInfo {
     if (value == null) {
       writer.writeFieldName(this, index);
       writer.writeNull();
+    } else if (index == 0) {
+      // Keep interpreted packable fields on the same concrete writer entry as 
generated codecs.
+      // A separate byte-array entry leaves the packed encoder cold until 
generated parents compile,
+      // allowing C2 to copy the complete encoder graph into those parents 
instead.
+      if (utf8NamePrefix.length <= Long.BYTES * 2) {
+        writer.writeStringField(
+            utf8NamePrefixWord0, utf8NamePrefixWord1, utf8NamePrefix.length, 
value);
+      } else {
+        writer.writeStringField(utf8NamePrefix, value);
+      }
+    } else if (utf8CommaNamePrefix.length <= Long.BYTES * 2) {
+      writer.writeStringField(
+          utf8CommaNamePrefixWord0, utf8CommaNamePrefixWord1, 
utf8CommaNamePrefix.length, value);
     } else {
-      writer.writeStringField(utf8NamePrefix, utf8CommaNamePrefix, index, 
value);
+      writer.writeStringField(utf8CommaNamePrefix, value);
     }
     return true;
   }
@@ -1193,4 +1214,13 @@ public final class JsonFieldInfo {
     }
     return utf16;
   }
+
+  private static long packedPrefixWord(byte[] prefix, int offset) {
+    long word = 0;
+    int end = Math.min(prefix.length, offset + Long.BYTES);
+    for (int i = offset; i < end; i++) {
+      word |= (prefix[i] & 0xffL) << ((i - offset) << 3);
+    }
+    return word;
+  }
 }
diff --git 
a/java/fory-json/src/main/java/org/apache/fory/json/writer/StringJsonWriter.java
 
b/java/fory-json/src/main/java/org/apache/fory/json/writer/StringJsonWriter.java
index 0be50cbf9..d2dd423eb 100644
--- 
a/java/fory-json/src/main/java/org/apache/fory/json/writer/StringJsonWriter.java
+++ 
b/java/fory-json/src/main/java/org/apache/fory/json/writer/StringJsonWriter.java
@@ -657,25 +657,25 @@ public final class StringJsonWriter extends JsonWriter 
implements Appendable {
     writeIntUtf16NoEnsure(value);
   }
 
-  public void writeObjectIntField(byte[] namePrefix, int value) {
+  public void writeObjectStartWithIntField(byte[] namePrefix, int value) {
     enterDepth();
     if (coder == LATIN1) {
-      writeObjectIntFieldLatin1(namePrefix, value);
+      writeObjectStartWithIntFieldLatin1(namePrefix, value);
       return;
     }
-    writeObjectIntFieldUtf16(namePrefix, value);
+    writeObjectStartWithIntFieldUtf16(namePrefix, value);
   }
 
-  public void writeObjectIntField(byte[] namePrefix, byte[] utf16NamePrefix, 
int value) {
+  public void writeObjectStartWithIntField(byte[] namePrefix, byte[] 
utf16NamePrefix, int value) {
     enterDepth();
     if (coder == LATIN1) {
-      writeObjectIntFieldLatin1(namePrefix, value);
+      writeObjectStartWithIntFieldLatin1(namePrefix, value);
       return;
     }
-    writeObjectIntFieldUtf16Value(utf16NamePrefix, value);
+    writeObjectStartWithIntFieldUtf16Value(utf16NamePrefix, value);
   }
 
-  public void writeObjectIntField(
+  public void writeObjectStartWithIntField(
       byte[] namePrefix,
       long utf16Prefix0,
       long utf16Prefix1,
@@ -685,35 +685,35 @@ public final class StringJsonWriter extends JsonWriter 
implements Appendable {
       int value) {
     enterDepth();
     if (coder == LATIN1) {
-      writeObjectIntFieldLatin1(namePrefix, value);
+      writeObjectStartWithIntFieldLatin1(namePrefix, value);
       return;
     }
-    writeObjectIntFieldUtf16Packed(
+    writeObjectStartWithIntFieldUtf16Packed(
         utf16Prefix0, utf16Prefix1, utf16Prefix2, utf16Prefix3, 
utf16PrefixLength, value);
   }
 
-  private void writeObjectIntFieldLatin1(byte[] namePrefix, int value) {
+  private void writeObjectStartWithIntFieldLatin1(byte[] namePrefix, int 
value) {
     ensure(namePrefix.length + 12);
     buffer[position++] = (byte) '{';
     writeRawLatin1NoEnsure(namePrefix);
     writeIntNoEnsure(value);
   }
 
-  private void writeObjectIntFieldUtf16(byte[] namePrefix, int value) {
+  private void writeObjectStartWithIntFieldUtf16(byte[] namePrefix, int value) 
{
     ensure(((namePrefix.length + 1) << 1) + 22);
     writeUtf16ByteNoEnsure((byte) '{');
     writeRawUtf16NoEnsure(namePrefix);
     writeIntUtf16NoEnsure(value);
   }
 
-  private void writeObjectIntFieldUtf16Value(byte[] utf16NamePrefix, int 
value) {
+  private void writeObjectStartWithIntFieldUtf16Value(byte[] utf16NamePrefix, 
int value) {
     ensure(utf16NamePrefix.length + 24);
     writeUtf16ByteNoEnsure((byte) '{');
     writeRawUtf16ValueNoEnsure(utf16NamePrefix);
     writeIntUtf16NoEnsure(value);
   }
 
-  private void writeObjectIntFieldUtf16Packed(
+  private void writeObjectStartWithIntFieldUtf16Packed(
       long utf16Prefix0,
       long utf16Prefix1,
       long utf16Prefix2,
@@ -809,25 +809,25 @@ public final class StringJsonWriter extends JsonWriter 
implements Appendable {
     writeLongUtf16NoEnsure(value);
   }
 
-  public void writeObjectLongField(byte[] namePrefix, long value) {
+  public void writeObjectStartWithLongField(byte[] namePrefix, long value) {
     enterDepth();
     if (coder == LATIN1) {
-      writeObjectLongFieldLatin1(namePrefix, value);
+      writeObjectStartWithLongFieldLatin1(namePrefix, value);
       return;
     }
-    writeObjectLongFieldUtf16(namePrefix, value);
+    writeObjectStartWithLongFieldUtf16(namePrefix, value);
   }
 
-  public void writeObjectLongField(byte[] namePrefix, byte[] utf16NamePrefix, 
long value) {
+  public void writeObjectStartWithLongField(byte[] namePrefix, byte[] 
utf16NamePrefix, long value) {
     enterDepth();
     if (coder == LATIN1) {
-      writeObjectLongFieldLatin1(namePrefix, value);
+      writeObjectStartWithLongFieldLatin1(namePrefix, value);
       return;
     }
-    writeObjectLongFieldUtf16Value(utf16NamePrefix, value);
+    writeObjectStartWithLongFieldUtf16Value(utf16NamePrefix, value);
   }
 
-  public void writeObjectLongField(
+  public void writeObjectStartWithLongField(
       byte[] namePrefix,
       long utf16Prefix0,
       long utf16Prefix1,
@@ -837,35 +837,35 @@ public final class StringJsonWriter extends JsonWriter 
implements Appendable {
       long value) {
     enterDepth();
     if (coder == LATIN1) {
-      writeObjectLongFieldLatin1(namePrefix, value);
+      writeObjectStartWithLongFieldLatin1(namePrefix, value);
       return;
     }
-    writeObjectLongFieldUtf16Packed(
+    writeObjectStartWithLongFieldUtf16Packed(
         utf16Prefix0, utf16Prefix1, utf16Prefix2, utf16Prefix3, 
utf16PrefixLength, value);
   }
 
-  private void writeObjectLongFieldLatin1(byte[] namePrefix, long value) {
+  private void writeObjectStartWithLongFieldLatin1(byte[] namePrefix, long 
value) {
     ensure(namePrefix.length + 21);
     buffer[position++] = (byte) '{';
     writeRawLatin1NoEnsure(namePrefix);
     writeLongNoEnsure(value);
   }
 
-  private void writeObjectLongFieldUtf16(byte[] namePrefix, long value) {
+  private void writeObjectStartWithLongFieldUtf16(byte[] namePrefix, long 
value) {
     ensure(((namePrefix.length + 1) << 1) + 40);
     writeUtf16ByteNoEnsure((byte) '{');
     writeRawUtf16NoEnsure(namePrefix);
     writeLongUtf16NoEnsure(value);
   }
 
-  private void writeObjectLongFieldUtf16Value(byte[] utf16NamePrefix, long 
value) {
+  private void writeObjectStartWithLongFieldUtf16Value(byte[] utf16NamePrefix, 
long value) {
     ensure(utf16NamePrefix.length + 42);
     writeUtf16ByteNoEnsure((byte) '{');
     writeRawUtf16ValueNoEnsure(utf16NamePrefix);
     writeLongUtf16NoEnsure(value);
   }
 
-  private void writeObjectLongFieldUtf16Packed(
+  private void writeObjectStartWithLongFieldUtf16Packed(
       long utf16Prefix0,
       long utf16Prefix1,
       long utf16Prefix2,
@@ -2208,11 +2208,18 @@ public final class StringJsonWriter extends JsonWriter 
implements Appendable {
     return ch > 0x1F && ch != '"' && ch != '\\';
   }
 
+  // Keep the exact uncommon fallback outside these per-word predicates. 
Folding it back in makes
+  // the standalone predicates too large for C2 to inline into the 
compact-string writers.
   private static boolean isJsonAsciiWord(long word) {
     long notBackslash = ((word ^ BACKSLASH_BYTES_COMPLEMENT) + ONE_BYTES) & 
HIGH_BITS;
     if ((notBackslash & (word + ASCII_GT_QUOTE_OFFSET)) == HIGH_BITS) {
       return true;
     }
+    return isJsonAsciiWordFallback(word);
+  }
+
+  private static boolean isJsonAsciiWordFallback(long word) {
+    long notBackslash = ((word ^ BACKSLASH_BYTES_COMPLEMENT) + ONE_BYTES) & 
HIGH_BITS;
     return (((word + ASCII_CONTROL_OFFSET) & ~word) & HIGH_BITS) == HIGH_BITS
         && (((word ^ QUOTE_BYTES_COMPLEMENT) + ONE_BYTES) & HIGH_BITS) == 
HIGH_BITS
         && notBackslash == HIGH_BITS;
@@ -2229,6 +2236,10 @@ public final class StringJsonWriter extends JsonWriter 
implements Appendable {
         == HIGH_BITS) {
       return true;
     }
+    return isJsonAsciiWordsFallback(word0, word1, notBackslash);
+  }
+
+  private static boolean isJsonAsciiWordsFallback(long word0, long word1, long 
notBackslash) {
     return ((word0 + ASCII_CONTROL_OFFSET)
             & (word1 + ASCII_CONTROL_OFFSET)
             & ((word0 ^ QUOTE_BYTES_COMPLEMENT) + ONE_BYTES)
@@ -2242,6 +2253,10 @@ public final class StringJsonWriter extends JsonWriter 
implements Appendable {
     if ((notBackslash & (word + INT_ASCII_GT_QUOTE_OFFSET)) == INT_HIGH_BITS) {
       return true;
     }
+    return isJsonAsciiIntFallback(word, notBackslash);
+  }
+
+  private static boolean isJsonAsciiIntFallback(int word, int notBackslash) {
     return (((word + INT_ASCII_CONTROL_OFFSET) & ~word) & INT_HIGH_BITS) == 
INT_HIGH_BITS
         && (((word ^ INT_QUOTE_BYTES_COMPLEMENT) + INT_ONE_BYTES) & 
INT_HIGH_BITS) == INT_HIGH_BITS
         && notBackslash == INT_HIGH_BITS;
diff --git 
a/java/fory-json/src/main/java/org/apache/fory/json/writer/Utf8JsonWriter.java 
b/java/fory-json/src/main/java/org/apache/fory/json/writer/Utf8JsonWriter.java
index 0808b8029..1bad1f562 100644
--- 
a/java/fory-json/src/main/java/org/apache/fory/json/writer/Utf8JsonWriter.java
+++ 
b/java/fory-json/src/main/java/org/apache/fory/json/writer/Utf8JsonWriter.java
@@ -562,7 +562,7 @@ public final class Utf8JsonWriter extends JsonWriter 
implements Appendable {
     writeIntNoEnsure(value);
   }
 
-  public void writeObjectIntField(byte[] namePrefix, int value) {
+  public void writeObjectStartWithIntField(byte[] namePrefix, int value) {
     enterDepth();
     ensure(namePrefix.length + 12);
     buffer[position++] = (byte) '{';
@@ -570,7 +570,8 @@ public final class Utf8JsonWriter extends JsonWriter 
implements Appendable {
     writeIntNoEnsure(value);
   }
 
-  public void writeObjectIntField(long prefix0, long prefix1, int 
prefixLength, int value) {
+  public void writeObjectStartWithIntField(
+      long prefix0, long prefix1, int prefixLength, int value) {
     enterDepth();
     ensurePackedPrefix(prefixLength, 12);
     buffer[position++] = (byte) '{';
@@ -595,7 +596,7 @@ public final class Utf8JsonWriter extends JsonWriter 
implements Appendable {
     writeLongNoEnsure(value);
   }
 
-  public void writeObjectLongField(byte[] namePrefix, long value) {
+  public void writeObjectStartWithLongField(byte[] namePrefix, long value) {
     enterDepth();
     ensure(namePrefix.length + 21);
     buffer[position++] = (byte) '{';
@@ -603,7 +604,8 @@ public final class Utf8JsonWriter extends JsonWriter 
implements Appendable {
     writeLongNoEnsure(value);
   }
 
-  public void writeObjectLongField(long prefix0, long prefix1, int 
prefixLength, long value) {
+  public void writeObjectStartWithLongField(
+      long prefix0, long prefix1, int prefixLength, long value) {
     enterDepth();
     ensurePackedPrefix(prefixLength, 21);
     buffer[position++] = (byte) '{';
@@ -611,6 +613,12 @@ public final class Utf8JsonWriter extends JsonWriter 
implements Appendable {
     writeLongNoEnsure(value);
   }
 
+  public void writeObjectStartWithStringField(
+      long prefix0, long prefix1, int prefixLength, String value) {
+    enterDepth();
+    writeStringField(prefix0, prefix1, prefixLength, value);
+  }
+
   public void writeStringField(byte[] namePrefix, byte[] commaNamePrefix, int 
index, String value) {
     byte[] prefix = index == 0 ? namePrefix : commaNamePrefix;
     writeStringField(prefix, value);
@@ -1610,6 +1618,8 @@ public final class Utf8JsonWriter extends JsonWriter 
implements Appendable {
     return ch > 0x1F && ch < 0x80 && ch != '"' && ch != '\\';
   }
 
+  // Keep the exact uncommon fallback outside these per-word predicates. 
Folding it back in makes
+  // the standalone predicates too large for C2 to inline into the 
short-string writers.
   private static boolean isJsonAsciiWord(long word) {
     long notBackslashMask = ((word ^ BACKSLASH_BYTES_COMPLEMENT) + ONE_BYTES) 
& HIGH_BITS;
     // Common unescaped bytes are greater than '"' and not '\\'. The fallback 
keeps the exact
@@ -1617,6 +1627,11 @@ public final class Utf8JsonWriter extends JsonWriter 
implements Appendable {
     if ((notBackslashMask & (word + ASCII_GT_QUOTE_OFFSET)) == HIGH_BITS) {
       return true;
     }
+    return isJsonAsciiWordFallback(word);
+  }
+
+  private static boolean isJsonAsciiWordFallback(long word) {
+    long notBackslashMask = ((word ^ BACKSLASH_BYTES_COMPLEMENT) + ONE_BYTES) 
& HIGH_BITS;
     return (((word + ASCII_CONTROL_OFFSET) & ~word) & HIGH_BITS) == HIGH_BITS
         && (((word ^ QUOTE_BYTES_COMPLEMENT) + ONE_BYTES) & HIGH_BITS) == 
HIGH_BITS
         && notBackslashMask == HIGH_BITS;
@@ -1633,12 +1648,7 @@ public final class Utf8JsonWriter extends JsonWriter 
implements Appendable {
         == HIGH_BITS) {
       return true;
     }
-    return ((word0 + ASCII_CONTROL_OFFSET)
-            & (word1 + ASCII_CONTROL_OFFSET)
-            & ((word0 ^ QUOTE_BYTES_COMPLEMENT) + ONE_BYTES)
-            & ((word1 ^ QUOTE_BYTES_COMPLEMENT) + ONE_BYTES)
-            & notBackslashMask)
-        == HIGH_BITS;
+    return isJsonAsciiWordsFallback(word0, word1, notBackslashMask);
   }
 
   private static boolean isJsonAsciiWords(long word0, long word1, long word2) {
@@ -1654,14 +1664,7 @@ public final class Utf8JsonWriter extends JsonWriter 
implements Appendable {
         == HIGH_BITS) {
       return true;
     }
-    return ((word0 + ASCII_CONTROL_OFFSET)
-            & (word1 + ASCII_CONTROL_OFFSET)
-            & (word2 + ASCII_CONTROL_OFFSET)
-            & ((word0 ^ QUOTE_BYTES_COMPLEMENT) + ONE_BYTES)
-            & ((word1 ^ QUOTE_BYTES_COMPLEMENT) + ONE_BYTES)
-            & ((word2 ^ QUOTE_BYTES_COMPLEMENT) + ONE_BYTES)
-            & notBackslashMask)
-        == HIGH_BITS;
+    return isJsonAsciiWordsFallback(word0, word1, word2, notBackslashMask);
   }
 
   private static boolean isJsonAsciiWords(long word0, long word1, long word2, 
long word3) {
@@ -1679,6 +1682,32 @@ public final class Utf8JsonWriter extends JsonWriter 
implements Appendable {
         == HIGH_BITS) {
       return true;
     }
+    return isJsonAsciiWordsFallback(word0, word1, word2, word3, 
notBackslashMask);
+  }
+
+  private static boolean isJsonAsciiWordsFallback(long word0, long word1, long 
notBackslashMask) {
+    return ((word0 + ASCII_CONTROL_OFFSET)
+            & (word1 + ASCII_CONTROL_OFFSET)
+            & ((word0 ^ QUOTE_BYTES_COMPLEMENT) + ONE_BYTES)
+            & ((word1 ^ QUOTE_BYTES_COMPLEMENT) + ONE_BYTES)
+            & notBackslashMask)
+        == HIGH_BITS;
+  }
+
+  private static boolean isJsonAsciiWordsFallback(
+      long word0, long word1, long word2, long notBackslashMask) {
+    return ((word0 + ASCII_CONTROL_OFFSET)
+            & (word1 + ASCII_CONTROL_OFFSET)
+            & (word2 + ASCII_CONTROL_OFFSET)
+            & ((word0 ^ QUOTE_BYTES_COMPLEMENT) + ONE_BYTES)
+            & ((word1 ^ QUOTE_BYTES_COMPLEMENT) + ONE_BYTES)
+            & ((word2 ^ QUOTE_BYTES_COMPLEMENT) + ONE_BYTES)
+            & notBackslashMask)
+        == HIGH_BITS;
+  }
+
+  private static boolean isJsonAsciiWordsFallback(
+      long word0, long word1, long word2, long word3, long notBackslashMask) {
     return ((word0 + ASCII_CONTROL_OFFSET)
             & (word1 + ASCII_CONTROL_OFFSET)
             & (word2 + ASCII_CONTROL_OFFSET)
@@ -1697,6 +1726,10 @@ public final class Utf8JsonWriter extends JsonWriter 
implements Appendable {
     if ((notBackslashMask & (word + INT_ASCII_GT_QUOTE_OFFSET)) == 
INT_HIGH_BITS) {
       return true;
     }
+    return isJsonAsciiIntFallback(word, notBackslashMask);
+  }
+
+  private static boolean isJsonAsciiIntFallback(int word, int 
notBackslashMask) {
     return (((word + INT_ASCII_CONTROL_OFFSET) & ~word) & INT_HIGH_BITS) == 
INT_HIGH_BITS
         && (((word ^ INT_QUOTE_BYTES_COMPLEMENT) + INT_ONE_BYTES) & 
INT_HIGH_BITS) == INT_HIGH_BITS
         && notBackslashMask == INT_HIGH_BITS;
@@ -1708,6 +1741,10 @@ public final class Utf8JsonWriter extends JsonWriter 
implements Appendable {
     if ((notBackslashMask & (word + SHORT_ASCII_GT_QUOTE_OFFSET)) == 
SHORT_HIGH_BITS) {
       return true;
     }
+    return isJsonAsciiShortFallback(word, notBackslashMask);
+  }
+
+  private static boolean isJsonAsciiShortFallback(int word, int 
notBackslashMask) {
     return (((word + SHORT_ASCII_CONTROL_OFFSET) & ~word) & SHORT_HIGH_BITS) 
== SHORT_HIGH_BITS
         && (((word ^ SHORT_QUOTE_BYTES_COMPLEMENT) + SHORT_ONE_BYTES) & 
SHORT_HIGH_BITS)
             == SHORT_HIGH_BITS


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


Reply via email to