Ben Weidig created TAP5-2837:
--------------------------------

             Summary: Improve JSON serialization performance 
(toString/toCompactString)
                 Key: TAP5-2837
                 URL: https://issues.apache.org/jira/browse/TAP5-2837
             Project: Tapestry 5
          Issue Type: Improvement
          Components: tapestry-json
    Affects Versions: 5.10.0
            Reporter: Ben Weidig


h3. Overview

{{JSONObject}}/{{JSONArray}}'s {{toString()}} and {{toCompactString()}} are 
slower than necessary due to synchronization overhead and per-{{String}} 
allocation in the print path.

My sandbox implementation at 
https://github.com/benweidig/tapestry-json-improvements (non-breaking, verified 
byte-for-byte against current output) shows a ~4-6x speedup across payload 
sizes and both compact/pretty formatting, with no public API changes.

h3. Problem

* {{toString()}}/{{toCompactString()}} route output through {{CharArrayWriter}} 
wrapped in {{PrintWriter}}, both use {{synchronized}} methods on every write. 
This is pure overhead for single-threaded string building.
* {{JSONObject.quote(String)}} allocates a new {{JSONStringer}} (own 
{{StringBuilder}} + {{ArrayList}} scope stack) for every quoted string, like 
keys and string values in a payload.
* Extended-Unicode escaping goes through {{String.format}}, which parses the 
format string on every call.
* Output buffers start at JDK default capacity with no pre-sizing, causing 
repeated grow/copy for larger trees.

h3. Proposed changes

Delete {{JSONStringer}} and inline its escaping loop directly into 
{{JSONObject}} as a static {{escapeInto(...)}} helper, called directly by 
{{quote()}} and the print sessions.

Replace {{String.format}} hex escaping with manual nibble extraction + lookup 
table.

Drop {{CharArrayWriter}}/{{PrintWriter}} for 
{{toString()}}/{{toCompactString()}}.
Instead, build directly into a {{StringBuilder}}.

Pre-size the output buffer based on collection size instead of the JDK default.

All changes are additive/internal/package-private only, without any no 
signature changes, no behavior changes.
Full test suite plus a new baseline-equivalence test confirmed identical output 
to 5.9.1 in my sandbox.

h3. Risks

* Low functional risk: escaping logic is a straight extraction/refactor, 
covered by existing tests.
* Removing {{JSONStringer}} is a *breaking change* for any external caller 
referencing that class directly (it's currently public): Needs a 
compatibility/deprecation decision before merge.




--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to