Skip to content

Eliminate double-serialization in SystemTextJsonLanguageServer#83417

Open
ToddGrun wants to merge 1 commit intodotnet:mainfrom
ToddGrun:dev/toddgrun/lsp-eliminate-double-serialization
Open

Eliminate double-serialization in SystemTextJsonLanguageServer#83417
ToddGrun wants to merge 1 commit intodotnet:mainfrom
ToddGrun:dev/toddgrun/lsp-eliminate-double-serialization

Conversation

@ToddGrun
Copy link
Copy Markdown
Contributor

@ToddGrun ToddGrun commented Apr 26, 2026

SystemTextJsonDelegatingEntryPoint.ExecuteRequestAsync was calling JsonSerializer.SerializeToElement(result) to convert handler results into a JsonElement before returning to StreamJsonRpc. This caused a full serialize → byte[] → JsonDocument → JsonElement round-trip, only for StreamJsonRpc to immediately re-serialize that JsonElement back to wire bytes. Every LSP response paid this cost.

The fix returns the handler's typed result directly as object. StreamJsonRpc serializes it to the wire using JsonSerializer.Serialize(writer, value, declaredType, options), which invokes the same runtime-type converters — producing identical JSON output with zero intermediate allocations.

Why this is safe: The previous SerializeToElement(result, options) already received result as object (from InvokeAsync), so it was already relying on STJ's object→runtime-type dispatch. We're removing the redundant intermediate step, not changing the serialization path. The entry point methods are private, accessed only via MethodInfo name-based reflection — return type changes don't affect lookup.

PIT impact: Identified via RazorEditingTests.CompletionInCohostingForComponents, scenario 0300.Completion. Eliminates ~12 MB of System.Byte[] and JsonDocument allocations in the devenv process from semantic tokens and completion responses.

*** Before ***
image

*** After ***
image

Microsoft Reviewers: Open in CodeFlow

SystemTextJsonDelegatingEntryPoint.ExecuteRequestAsync was calling JsonSerializer.SerializeToElement(result) to convert handler results into a JsonElement before returning to StreamJsonRpc. This caused a full serialize → byte[] → JsonDocument → JsonElement round-trip, only for StreamJsonRpc to immediately re-serialize that JsonElement back to wire bytes. Every LSP response paid this cost.

The fix returns the handler's typed result directly as object. StreamJsonRpc serializes it to the wire using JsonSerializer.Serialize(writer, value, declaredType, options), which invokes the same runtime-type converters — producing identical JSON output with zero intermediate allocations.

Why this is safe: The previous SerializeToElement(result, options) already received result as object (from InvokeAsync), so it was already relying on STJ's object→runtime-type dispatch. We're removing the redundant intermediate step, not changing the serialization path. The entry point methods are private, accessed only via MethodInfo name-based reflection — return type changes don't affect lookup.

PIT impact: Identified via RazorEditingTests.CompletionInCohostingForComponents, scenario 0300.Completion. Eliminates ~12 MB of System.Byte[] and JsonDocument allocations in the devenv process from semantic tokens and completion responses.
@ToddGrun ToddGrun requested a review from a team as a code owner April 26, 2026 16:34
@ToddGrun
Copy link
Copy Markdown
Contributor Author

/pr-val

@github-actions
Copy link
Copy Markdown
Contributor

View PR Validation Run triggered by @ToddGrun

Parameters
  • Validation Type: pr-val
  • Pipeline ID: 8972
  • Pipeline Version: main
  • PR Number: 83417
  • Commit SHA: 2a5c95f52eb57c551122aa77f7faaed4b75b3961
  • Source Branch: dev/toddgrun/lsp-eliminate-double-serialization
  • Target Branch: main
  • Build ID: 13940660

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant