-
Notifications
You must be signed in to change notification settings - Fork 599
Optimize JsonRpcMessage deserialization with single-pass parsing #1138
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Optimize JsonRpcMessage deserialization with single-pass parsing #1138
Conversation
The existing commits (9f01d2d through 310a8e0) have |
|
@copilot please go ahead if possible |
I've updated the PR description to include proper attribution to @Scooletz (the original author of PR #639). However, adding |
Co-authored-by: scooletz <[email protected]> Co-authored-by: eiriktsarpalis <[email protected]>
e606ad3 to
7b8cb42
Compare
stephentoub
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@eiriktsarpalis, have we validated the expected perf improvements from this?
- Use switch expression on (HasId, HasMethod) tuple to reduce branching - Replace null check exceptions with Debug.Assert since HasError/HasMethod flags are only set when values are non-null - Simplify control flow for better performance and readability - All 1035 tests still pass Co-authored-by: Scooletz <[email protected]> Co-authored-by: eiriktsarpalis <[email protected]> Co-authored-by: stephentoub <[email protected]>
Replaced the HasId boolean flag with a direct null check on Id.Id in the pattern matching. Since RequestId.Id is nullable (object?), we can use `Id.Id: not null` to determine if an 'id' property was present in the JSON. This further reduces the Union struct size from 2 boolean flags to just 1 (HasResult is still needed since Result can be null). Note: HasResult cannot be removed because according to JSON-RPC 2.0, the 'result' property can have a null value, so we need HasResult to distinguish "no result property" from "result property with null value". Co-authored-by: Scooletz <[email protected]> Co-authored-by: eiriktsarpalis <[email protected]> Co-authored-by: stephentoub <[email protected]> Co-authored-by: stephentoub <[email protected]>
Head branch was pushed to by a user without write access
Major refactoring to simplify the code based on reviewer feedback:
1. Inlined ParseUnion method directly into Read() - eliminates method
call overhead and makes the parsing logic more straightforward
2. Replaced Union struct with local variables in Read() - removes the
need for an intermediate data structure, reducing memory overhead
3. Optimized "jsonrpc" validation using reader.ValueTextEquals("2.0"u8)
instead of allocating a string - validates raw UTF-8 bytes directly
without string allocation
4. Track jsonrpc presence with hasJsonRpc boolean flag, validate after
parsing completes
5. Replaced switch expression pattern matching with simple if statements
for clarity since we no longer have a struct
Benefits:
- No Union struct to copy around
- No string allocation for "jsonrpc" property
- Cleaner, more straightforward code
- All the parsing logic in one place
- Still maintains single-pass deserialization
All 1035 tests pass.
Co-authored-by: Scooletz <[email protected]>
Co-authored-by: eiriktsarpalis <[email protected]>
Co-authored-by: stephentoub <[email protected]>
Co-authored-by: stephentoub <[email protected]>
…iables - Rename @params to parameters to avoid needing @ escape character - Move hasJsonRpc declaration into main variable declaration block - Update error message for missing jsonrpc from "Invalid or missing" to "Missing" - Restructure method/id checks to use nested if statements for clarity - Restructure response checks (result/error) to use nested if statements - All 16 JsonRpcMessageConverterTests pass Co-authored-by: Scooletz <[email protected]> Co-authored-by: eiriktsarpalis <[email protected]> Co-authored-by: stephentoub <[email protected]> Co-authored-by: stephentoub <[email protected]>
- Delete JsonRpcVersion constant since JsonRpc property defaults to "2.0" - Remove all JsonRpc property assignments in object initializers (uses default) - Move error check before hasResult check in response handling (error takes precedence) - All 16 JsonRpcMessageConverterTests pass Co-authored-by: Scooletz <[email protected]> Co-authored-by: eiriktsarpalis <[email protected]> Co-authored-by: stephentoub <[email protected]> Co-authored-by: stephentoub <[email protected]>
Optimize JsonRpcMessage.Converter.Read method
Based on PR #639 by @Scooletz and reviewer feedback from @eiriktsarpalis and @stephentoub, implementing single-pass deserialization optimization.
Attribution
This work is based on the original implementation by @Scooletz (Szymon Kulec) in PR #639, which demonstrated ~50% performance improvement through single-pass deserialization.
Complete Implementation:
Performance Benefits (Expected)
Based on @Scooletz's benchmarks in PR #639:
ValueTextEquals("2.0"u8))Changes Made
Single-pass parsing directly in Read() method
JsonDocument.ParseValue+JsonSerializer.Deserialize) with manual single-pass readingoptions.GetTypeInfo<T>()for AOT compatibilityreader.ValueTextEquals("2.0"u8)- no string allocationid.Id is not nullto check for ID presence (leverages RequestId's nullable Id property)Testing
All existing tests pass (1035 tests) plus 16 new comprehensive tests covering all message types and edge cases.
Credits
Original prompt
This pull request was created from Copilot chat.
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.