WIP: DO NOT REVIEW Use generated regexes for gRPC JSON parsing#67758
WIP: DO NOT REVIEW Use generated regexes for gRPC JSON parsing#67758artl93 wants to merge 2 commits into
Conversation
Replace the runtime-compiled timestamp and duration regexes with source-generated equivalents, add semantic differential coverage, and benchmark parser and JSON deserialization paths. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 61f16215-869e-496a-bb38-7175bebdd2db
Benchmark timestamp writing and field-mask validation alongside timestamp and duration parsing so generated-regex measurements include unaffected control paths. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 61f16215-869e-496a-bb38-7175bebdd2db
Final experiment evidence update — still WIPThe preliminary source-toggle evidence has been replaced with detached-SHA evidence and the PR body now contains the canonical cold and five-run crossover BDN tables. Corrections made after skeptical review:
Retained evidence: 800 cold sample rows ( Local validation: full gRPC build succeeded with 0 warnings/errors; 340 unit and 24 integration tests passed. PR CI is mostly green and still running. This PR intentionally remains DRAFT / WIP. DO NOT REVIEW and do not mark it ready. |
I am running an experiment - full analysis to follow. Contact @artl93
Summary
Replace the two process-wide
RegexOptions.Compiledtimestamp and duration regex fields used by gRPC JSON transcoding with byte-for-byte-equivalent[GeneratedRegex]patterns.The useful result is cold first use, not a broad steady-state throughput claim:
Why this changes first use
The current
Legacytype stores both regexes in static fields. Any path that requires those fields initializes and compiles both regexes at runtime. Timestamp serialization also reads the staticUnixEpochfield, so its first use pays the same type-initialization cost even though it does not match a regex. Once initialized, that same write path is regex-free and serves as a warm control.IsPathValiduses no static fields and stays neutral cold and warm.GeneratedRegexemits specializedRegexsubclasses and runners at build time. The generated methods return independent lazy singleton instances, avoiding runtime pattern parsing/dynamic-code setup and avoiding initialization of an unrelated pattern. The preservedRegexOptions.Compiledvalue remains observable inRegex.Options, but source generation emits the specialized runner at build time rather than reintroducing runtime IL emission.AddJsonTranscodingregistration itself does not callLegacy; this targets the first timestamp/duration transcoding request or first timestamp serialization, not service-registration time.This is most relevant to cold/serverless/scale-to-zero processes and first-request latency. Long-lived warm servers should expect little throughput change.
Clean fresh-process evidence
100 samples per implementation/path, alternating parent/candidate launch order.
processis wall-clock time around the complete child process.first operationis measured directly inside the child around the unchangedMethodInfo.Invoke; it is not derived by subtraction. The inner timer includes shared reflection overhead and one-time runtime/type/JIT work, so it diagnoses where cold work moves but does not represent warm request throughput. Allocation isGC.GetAllocatedBytesForCurrentThreadaround that same invocation.Complete-process detail (mean; median; run SD; 95% CI):
Direct first-operation detail (mean; median; run SD; 95% CI):
The one-time allocation savings do not recur per request; warm BDN allocations below are identical.
Warm BenchmarkDotNet evidence
Normal out-of-process BDN, concurrent server GC, .NET 11 Arm64. Five runs per implementation in crossover order: parent/candidate, candidate/parent, parent/candidate, candidate/parent, parent/candidate. The table uses paired per-run percent changes and a t interval across five run-level deltas.
ParseTimestampParseDurationDeserializeTimestampDeserializeDurationWriteTimestampcontrolValidateFieldMaskcontrolThe controls show roughly 1-2% candidate-side run bias. Therefore:
Semantic coverage
The generated regex is compared directly with the previous compiled regex for:
RegexOptions.Compiled, infinite timeout, and right-to-left state;en-US,tr-TR, andar-SAcurrent cultures;$final-newline behavior;+18:00/-18:00, invalid dates,-00:00, and range overflow;The pattern literals, options, and downstream parsing logic are otherwise unchanged. The regex accessors widen from private fields to internal methods solely so the friend test assembly can perform direct differential checks; there is no public API change.
AOT and trimming boundary
The emitted compiler source contains sealed generated
Regexsubclasses and specialized runners for both patterns; generated source SHA-256:4d7f30e982f5c4857eeceae284a129beb4a2e7c759e590dd2388c5e4a9d79569.The byte-identical regex probe source publishes and passes differential behavior under the installed .NET 10 NativeAOT toolchain. Publishing the net11 probe was attempted but the exact preview.6 runtime/NativeAOT packs are no longer available from configured feeds. Therefore this is not a claim that the full net11 gRPC package was NativeAOT-published. The narrower claim is that source generation removes runtime regex parsing/dynamic code generation and is structurally friendlier to trimming/AOT than runtime compilation.
Provenance and retained evidence
f297235c48c2cffc8fb61ff41803d709b30ddab2.c50f3fc2506f461298dc6b6d2e3db07c6682cccf.3cd8f078952eb2aa3197b0466dca14f3b0565ae1.eed49381a6f7d65558d63acbb4b940e67c42bfdf2b659f8ed8a7c4d0e075cedf.52c461b1160e027febbfdb4d05b26d8e6836e0fb69ea85623c9939438939606e(142,848 B); candidate66f132ea23859374213ebffd3178b846638144e8cf6ee183dddb189138fe0fe1(147,456 B).c56485cd6abea94e8485034b982a020935701b4a90b47bfa03a584cd6d3793d5.550f284d72b4809be6ad8e1b61729283a43c9fd9b163f24f996080229dd861b2.The evidence bundle is local session evidence and is not committed to this source-only PR. Canonical tables and hashes are included here so the coordinator can archive the raw bundle separately.
Validation
./src/Grpc/build.sh -test: succeeded, 0 warnings, 0 errors.Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests: 340 passed, 0 failed.Microsoft.AspNetCore.Grpc.JsonTranscoding.IntegrationTests: 24 passed, 0 failed.Tradeoffs and alternatives
RegexOptions.Compiledfields preserves smaller output but retains the measured cold cost.RegexOptions.NonBacktrackingwas not introduced because that would change engine/capture behavior; this change preserves existing options and semantics.Status
Still WIP. Do not review. Do not mark ready.