perf: reduce parse node allocations when deserializing primitive types#180
perf: reduce parse node allocations when deserializing primitive types#180
Conversation
Co-authored-by: baywet <7905502+baywet@users.noreply.github.com>
|
@copilot here is the pull request I want you to use as an example and I forgot to include earlier microsoft/kiota-dotnet#678 |
Thanks for the reference! I've already implemented the equivalent optimization in commit f6e946b. The C# PR's approach is: add private helpers that accept Go's
This achieves the same outcome: for an array of N primitives, we save N |
|
baywet
left a comment
There was a problem hiding this comment.
@copilot the coverage for the following methods is low, add additional unit tests:
- JsonParseNode tokenToValue
- JsonParseNode loadJsonTreeFromToken
- JsonParseNode GetObjectValue (the untyped nodes section)
- JsonParseNode getPrimitiveValue
…ctValue untyped section, and getPrimitiveValue Co-authored-by: baywet <7905502+baywet@users.noreply.github.com>
Added in commit af6326f. Coverage now includes:
|


Each primitive value (string, number, bool) in a parsed JSON tree previously received its own
*JsonParseNodeheap allocation in addition to a raw value allocation. For arrays of N primitives this meant 2N allocations; unknown object properties also unnecessarily allocated a node that was immediately discarded.Changes
Internal representation (
loadJsonTree)loadJsonTree+loadJsonTreeFromToken+tokenToValuetokenToValuestores primitives as raw Go values (*float64,*string,*bool, …) without a*JsonParseNodewrapper; complex values (objects/arrays) still produce*JsonParseNode[]*JsonParseNode→[]interface{}map[string]*JsonParseNode→map[string]interface{}Accessor updates
GetChildNodemap[string]interface{}; wraps raw primitives in a node on-demandGetObjectValue*JsonParseNodeon demand. For unknown fields (additionalData): stores raw value directly — zero node allocationGetCollectionOfPrimitiveValuesrawToPrimitiveValuehelper, skipping intermediate nodeGetCollectionOfEnumValues*stringelements directly without wrappingGetCollectionOfObjectValues*JsonParseNodefor each element (objects are still wrapped)GetRawValue[]interface{}andmap[string]interface{}New helpers
rawToPrimitiveValue— converts a stored raw value to the requested target type viaas(), avoiding a temporary*JsonParseNodeper collection elementrawToUntypedNodeable— converts raw primitives directly toabsser.UntypedNodeablefor untyped node treesAllocation delta (example: 20-element int array)
*JsonParseNode+ 20*float64*float64GetCollectionOfPrimitiveValues*int32*int32No public API surfaces were changed.
Tests
Added unit tests for the new and updated internal functions:
tokenToValue: all token types —float64,string,bool,json.Number(int, float, invalid),nil, unknown type,{and[delimitersloadJsonTreeFromToken: all primitive token types, object/array delimiters with primitive values, null values, and nested structuresGetObjectValue(untyped nodes section): all raw primitive types (*bool,*string,*float32,*float64,*int32,*int64), nil-valued node, map/array containing raw primitives and nested*JsonParseNodevaluesgetPrimitiveValue: nil node, nil value, unsupported type error, and all 14 valid target types (string,bool,uint8,byte,float32,float64,int32,int64,time,timeonly,dateonly,isoduration,uuid,base64)Original prompt
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.