Skip to content

perf(http-client-csharp): avoid Roslyn simplifier for type names#10916

Draft
ArcturusZhang wants to merge 10 commits into
microsoft:mainfrom
ArcturusZhang:arcturus/csharp-generator-type-name-resolver
Draft

perf(http-client-csharp): avoid Roslyn simplifier for type names#10916
ArcturusZhang wants to merge 10 commits into
microsoft:mainfrom
ArcturusZhang:arcturus/csharp-generator-type-name-resolver

Conversation

@ArcturusZhang

@ArcturusZhang ArcturusZhang commented Jun 8, 2026

Copy link
Copy Markdown
Member

This PR moves C# type-name simplification into the generator's write path so the common no-rewriter path can skip Roslyn Simplifier.ReduceAsync.

Changes

  • Collect CSharpType references per generated .cs file in CodeWriter.
  • Resolve type names per file, using simple names when safe and full namespace-qualified names when ambiguous.
  • Stop emitting global:: in the optimized path.
  • Skip Roslyn simplification when direct type-name resolution is enabled and no custom Roslyn rewriters are registered.
  • Add expression parenthesization support so generated code remains valid without Roslyn simplification.
  • Add GeneratedCodeWorkspaceNameResolverBenchmark covering writer + generated workspace post-processing.

Performance

Benchmark: GeneratedCodeWorkspaceNameResolverBenchmark with 100 models and 10 properties per model.

Method Mean Ratio Allocated Alloc Ratio
LegacyWriterWithRoslynSimplifier 594.5 ms 1.00 168.2 MB 1.00
OptimizedWriterWithoutRoslynSimplifier 202.4 ms 0.34 22.12 MB 0.13

This benchmark shows the optimized path is about 3x faster and allocates about 13% of the legacy path for the synthetic generated-workspace scenario.

Validation

  • dotnet build packages/http-client-csharp/generator
  • dotnet test packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Microsoft.TypeSpec.Generator.Tests.csproj --filter FullyQualifiedName!~GeneratorHandlerTests
  • dotnet test packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Microsoft.TypeSpec.Generator.ClientModel.Tests.csproj
  • dotnet run -c Release --framework net10.0 --filter *GeneratedCodeWorkspaceNameResolverBenchmark* from packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/perf

Emit C# type names through a per-file analyzer that collects referenced types, uses a namespace prefix trie to choose safe imports/qualifications, and omits global:: in the optimized path. Skip Roslyn Simplifier.ReduceAsync when no custom rewriters require it.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@microsoft-github-policy-service microsoft-github-policy-service Bot added the emitter:client:csharp Issue for the C# client emitter: @typespec/http-client-csharp label Jun 8, 2026
@pkg-pr-new

pkg-pr-new Bot commented Jun 8, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@typespec/http-client-csharp@10916

commit: b96896c

@github-actions

github-actions Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

No changes needing a change description found.

ArcturusZhang and others added 9 commits June 8, 2026 13:52
Resolve ambiguous type names with full namespace qualification instead of invalid namespace suffixes, and emit compound assignment statements without parentheses so generated code remains compilable without Roslyn simplification.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Keep expression statement writing unchanged for now and leave non-type Roslyn simplifications out of the type-name resolver change.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move per-file type reference collection into CodeWriter and add a BenchmarkDotNet benchmark comparing the legacy Roslyn simplifier path with the optimized resolver path.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Make CodeWriter collect file-local type references and construct CSharpTypeNameResolver per generated file instead of storing a global resolver built from all TypeProviders.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ifier

Use expression-context precedence when direct type-name resolution skips Roslyn simplification, keeping legacy formatting unchanged outside that path.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Write binary expressions without outer defensive parentheses and parenthesize nested expression nodes at expression-use sites instead.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace type checks in nested expression writing with a virtual ShouldParenthesize property on expression nodes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Write assignment values directly because C# assignment precedence preserves RHS grouping without requiring additional parentheses.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove the in-process benchmark override now that the expected .NET SDK is available for the perf project.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

emitter:client:csharp Issue for the C# client emitter: @typespec/http-client-csharp

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant