You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
🤖 This was created by Perf Improver, an automated AI assistant focused on performance improvements.
Goal and Rationale
Reduce heap allocations in AttributeExtensions.IsIgnored, which is called twice per test execution — once for the test class (ClassType.IsIgnored) and once for the test method (MethodInfo.IsIgnored). This means the allocations scale linearly with test count.
A yield iterator state machine from GetAttributes<ConditionBaseAttribute> (even when there are no ConditionBaseAttribute present)
A LINQ GroupBy operator object
After:
Attribute[]attributes=ReflectHelper.Instance.GetCustomAttributesCached(type);Dictionary<string,(boolSatisfied,string?FirstIgnoreMessage)>?groups=null;foreach(Attributeattrinattributes){if(attris not ConditionBaseAttributecondAttr){continue;}// ... manual grouping, groups allocated lazily only if ConditionBaseAttribute found}
Direct iteration of the cached Attribute[] — no iterator, no LINQ operator. The groups dictionary is allocated only when a ConditionBaseAttribute is present (the uncommon case). For most tests (no [Ignore], no OSCondition, etc.), zero allocations.
Performance Evidence
Methodology: Allocation count per IsIgnored call.
Scenario
Before (objects)
After
No ConditionBaseAttribute (common)
1 yield iterator + 1 GroupBy operator
0
[Ignore] present on class/method
1 yield iterator + LINQ Lookup internals
1 Dictionary
Per test execution (class + method)
~4 iterator/LINQ objects
0 (common case)
For a test suite with 1,000 tests: eliminates ~4,000 short-lived LINQ objects per run in the typical case.
Trade-offs
Manual grouping logic is slightly more verbose, but follows the established allocation-free pattern used by GetTestPropertiesAsTraits and GetTestCategories in the same codebase.
The Dictionary allocated in the uncommon case ([Ignore] present) replaces a LINQ Lookup<> — comparable or better allocation profile.
Semantics are identical: OR within group, AND across groups, first non-null ignore message reported.
✅ MSTestAdapter.PlatformServices builds with 0 warnings, 0 errors on net8.0.
Note: MSTestAdapter.PlatformServices.UnitTests has a pre-existing build failure in this environment due to AwesomeAssertions 9.3.0 only shipping netstandard2.1 (no net8.0 folder). This is unrelated to these changes. CI will run the full test suite.
Note
This was originally intended as a pull request, but GitHub Actions is not permitted to create or approve pull requests in this repository.
The changes have been pushed to branch perf-assist/eliminate-linq-allocations-isignored.
🤖 This was created by Perf Improver, an automated AI assistant focused on performance improvements.
Goal and Rationale
Reduce heap allocations in
AttributeExtensions.IsIgnored, which is called twice per test execution — once for the test class (ClassType.IsIgnored) and once for the test method (MethodInfo.IsIgnored). This means the allocations scale linearly with test count.Approach
Before:
Every call allocates:
GetAttributes<ConditionBaseAttribute>(even when there are noConditionBaseAttributepresent)GroupByoperator objectAfter:
Direct iteration of the cached
Attribute[]— no iterator, no LINQ operator. Thegroupsdictionary is allocated only when aConditionBaseAttributeis present (the uncommon case). For most tests (no[Ignore], noOSCondition, etc.), zero allocations.Performance Evidence
Methodology: Allocation count per
IsIgnoredcall.ConditionBaseAttribute(common)[Ignore]present on class/methodFor a test suite with 1,000 tests: eliminates ~4,000 short-lived LINQ objects per run in the typical case.
Trade-offs
GetTestPropertiesAsTraitsandGetTestCategoriesin the same codebase.Dictionaryallocated in the uncommon case ([Ignore]present) replaces a LINQLookup<>— comparable or better allocation profile.Reproducibility
Test Status
✅
MSTestAdapter.PlatformServicesbuilds with 0 warnings, 0 errors on net8.0.Note
This was originally intended as a pull request, but GitHub Actions is not permitted to create or approve pull requests in this repository.
The changes have been pushed to branch
perf-assist/eliminate-linq-allocations-isignored.Click here to create the pull request