perf: reuse argument-free attribute initializer text - #6788
Conversation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (2)
Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review. 📝 WalkthroughWalkthroughThe source generator now caches initializers for argument-free attributes by attribute type. Tests verify reuse, preservation of explicit arguments, and separation of constructed generic attribute types. ChangesAttribute initializer cache
Priority: ⬇️ Low Estimated code review effort: 2 (Simple) | ~10 minutes Change: Refactor Merge Risk: ⚪ Minimal · up to The initializer cache preserves per-use caller information and the covered argument and generic-type behaviors remain intact. No actionable merge risk remains. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. A rabbit finds a cache in bloom Comment |
Greptile SummaryThis PR reduces source-generator allocations by caching argument-free attribute initializer text per constructed attribute type within a compilation.
Confidence Score: 5/5The PR appears safe to merge; no actionable correctness, security, or repository-rule issues were identified. The cached argument-free initializer is determined entirely by its constructed attribute-type key, while all syntax carrying per-application values bypasses the new cache.
|
| Filename | Overview |
|---|---|
| src/TUnit.Core.SourceGenerator/CodeGenerators/Writers/AttributeWriter.cs | Adds a compilation-scoped cache for initializer text whose output is determined solely by the constructed attribute type. |
| tests/TUnit.Core.SourceGenerator.Tests/AttributeInitializerCacheTests.cs | Verifies that argument-free reuse does not affect explicit or named arguments and keeps constructed generic types distinct. |
Reviews (1): Last reviewed commit: "perf: reuse argument-free attribute init..." | Re-trigger Greptile
Review: perf: reuse argument-free attribute initializer text (#6788)Small, well-targeted change. Verified the core correctness question directly against Correctness of the new cache is sound. The new Named/explicit arguments correctly bypass the cache. Constructed generic attribute types are distinguished correctly. Minor, non-blocking observation: Tests: the two new tests ( Nit: the diff drops the UTF-8 BOM at the top of No blocking issues found. The benchmark methodology described in the PR body (byte-identical output validation across cold/edited compilation runs, plus regression tests for constructor-overload/type-argument behavior) is solid support for the allocation-reduction claim. |
Updated [TUnit.Core](https://github.com/thomhurst/TUnit) from 1.66.27 to 1.67.0. <details> <summary>Release notes</summary> _Sourced from [TUnit.Core's releases](https://github.com/thomhurst/TUnit/releases)._ ## 1.67.0 <!-- Release notes generated using configuration in .github/release.yml at v1.67.0 --> ## What's Changed ### Other Changes * docs: clarified and updated attributes comparison for xUnit 3 by @304NotModified in thomhurst/TUnit#6774 * perf: read inline argument metadata without reflection by @thomhurst in thomhurst/TUnit#6778 * perf: limit converter discovery to declarations by @thomhurst in thomhurst/TUnit#6779 * perf: skip teardown analysis when no disposable members need cleanup by @thomhurst in thomhurst/TUnit#6780 * perf: avoid line allocations when writing generated source by @thomhurst in thomhurst/TUnit#6781 * perf: avoid formatting interface names for data-source checks by @thomhurst in thomhurst/TUnit#6782 * perf: skip unannotated property data-source candidates by @thomhurst in thomhurst/TUnit#6784 * fix: fold inner exceptions into IDE test failure output by @thomhurst in thomhurst/TUnit#6777 * perf: reuse argument-free attribute initializer text by @thomhurst in thomhurst/TUnit#6788 * perf: extract test metadata in attribute transforms by @thomhurst in thomhurst/TUnit#6789 * perf: skip receiver registration for ordinary objects by @thomhurst in thomhurst/TUnit#6790 * perf: cache reporting properties on test contexts by @thomhurst in thomhurst/TUnit#6791 * fix: preserve executor registration, limiter precedence, and timeout classification by @Nice3point in thomhurst/TUnit#6768 ### Dependencies * chore(deps): update tunit to 1.66.27 by @thomhurst in thomhurst/TUnit#6742 * chore(deps): update dependency bunit to 2.10.3 by @thomhurst in thomhurst/TUnit#6745 * chore(deps): update dependency imposter to 0.1.11 by @thomhurst in thomhurst/TUnit#6744 * chore(deps): update dependency microsoft.kiota.abstractions to 2.1.2 by @thomhurst in thomhurst/TUnit#6747 * chore(deps): update dependency microsoft.templateengine.authoring.cli to v10.0.401 by @thomhurst in thomhurst/TUnit#6750 * chore(deps): update dependency fsharp.core to 10.1.401 by @thomhurst in thomhurst/TUnit#6748 * chore(deps): update dependency microsoft.templateengine.authoring.templateverifier to 10.0.401 by @thomhurst in thomhurst/TUnit#6751 * chore(deps): update dependency system.commandline to 2.0.12 by @thomhurst in thomhurst/TUnit#6752 * chore(deps): update dependency dotnet-sdk to v10.0.401 by @thomhurst in thomhurst/TUnit#6754 * chore(deps): update microsoft.extensions to 10.0.12 by @thomhurst in thomhurst/TUnit#6755 * chore(deps): update microsoft.aspnetcore to 10.0.12 by @thomhurst in thomhurst/TUnit#6753 * chore(deps): update dependency microsoft.entityframeworkcore to 10.0.12 by @thomhurst in thomhurst/TUnit#6749 * chore(deps): update mcr.microsoft.com/dotnet/sdk docker tag to v11 by @thomhurst in thomhurst/TUnit#6756 * chore(deps): update dependency microsoft.net.test.sdk to 18.10.0 by @thomhurst in thomhurst/TUnit#6761 * chore(deps): update microsoft.extensions to 10.10.0 by @thomhurst in thomhurst/TUnit#6762 * chore(deps): update react to ^19.3.0 by @thomhurst in thomhurst/TUnit#6763 * chore(deps): update dependency awssdk.sqs to 4.0.100.13 by @thomhurst in thomhurst/TUnit#6764 * chore(deps): update dependency polyfill to 11.3.0 by @thomhurst in thomhurst/TUnit#6765 * chore(deps): update dependency polyfill to 11.3.0 by @thomhurst in thomhurst/TUnit#6766 * chore(deps): update dependency stackexchange.redis to 3.2.0 by @thomhurst in thomhurst/TUnit#6769 * chore(deps): update dependency microsoft.net.stringtools to 18.10.1 by @thomhurst in thomhurst/TUnit#6771 * chore(deps): update dependency dotnet-trace to v10.0.745401 by @thomhurst in thomhurst/TUnit#6773 * chore(deps): bump colord from 2.9.3 to 2.10.0 in /docs by @dependabot[bot] in thomhurst/TUnit#6759 * chore(deps): bump joi from 17.13.4 to 17.13.7 in /docs by @dependabot[bot] in thomhurst/TUnit#6758 * chore(deps): bump js-yaml from 4.3.1 to 4.3.2 in /docs by @dependabot[bot] in thomhurst/TUnit#6757 * chore(deps): update dependency yaml to v2.9.1 by @thomhurst in thomhurst/TUnit#6785 * chore(deps): update verify to 32.0.1 by @thomhurst in thomhurst/TUnit#6786 * chore(deps): update dependency nunit.analyzers to 4.15.0 by @thomhurst in thomhurst/TUnit#6792 ## New Contributors * @304NotModified made their first contribution in thomhurst/TUnit#6774 * @Nice3point made their first contribution in thomhurst/TUnit#6768 ... (truncated) Commits viewable in [compare view](thomhurst/TUnit@v1.66.27...v1.67.0). </details> [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Large test suites repeatedly format identical argument-free attribute applications such as
[Test]. Cache the emitted initializer text by constructed attribute type within each compilation, while keeping explicit arguments and named properties on the existing per-application path. Optional caller-info parameters retain the same generated syntax as before.The complete TestMetadataGenerator benchmark (10,000 tests, 100 classes of 100 methods) allocates 12.87 MB less per generation, a 7.8% reduction. Mean elapsed time decreased from 112.5 ms to 107.5 ms, but the timing distributions overlap: this PR claims the allocation improvement, not a statistically established whole-build speedup.
Validation: generator snapshot suite on net10.0 passed (150 passed, one existing skip). Added regression checks for omitted versus explicit/named arguments and different constructed generic attribute types. The benchmark also compares all 100 generated files byte-for-byte between baseline and candidate for both original and edited compilations; output is identical. A separate executable compiled and passed all 10,000 generated tests.
Baseline:
656b66e723. Candidate:706200fd2c(final local-variable rename only after measurement). Generator assemblies built with repository-selected SDK 11.0.100-preview.7.26381.103. Benchmark host uses .NET 10.0.12, Roslyn 4.14.0 and BenchmarkDotNet 0.15.8 on Windows 11 / Intel i7-12700K. Runs are sequential, with no other builds or tests launched by this task during measurement. The separate-process BDN build stalled, so both versions use InProcessEmitToolchain and isolated AssemblyLoadContexts. Compilation construction is outside measurement; each operation runs the full generator from an unrun immutable driver. This measures generator work, not process startup or end-to-end MSBuild time.Reproduce the comparison
Create separate checkouts of the baseline and this PR. In an external working directory, save the project and Program.cs below as GeneratorBench. Use these commands (replace checkout paths):
GeneratorBench.csproj:
Program.cs:
Summary by CodeRabbit