-
-
Notifications
You must be signed in to change notification settings - Fork 109
Open
Description
Parent Epic
Part of #4159 - Performance Optimization: Hot Path Improvements
Problem
Various hot paths allocate intermediate arrays, create defensive copies, or use heap allocations where stack/pool alternatives exist.
Evidence
| File | Lines | Pattern | Suggested Fix |
|---|---|---|---|
TUnit.Engine/Discovery/ReflectionTestDataCollector.cs |
112, 125-126 | Task<List<T>>[] + merge allocations |
Use ArrayPool<Task> |
TUnit.Engine/Discovery/ReflectionTestDataCollector.cs |
140 | new List<TestMetadata>(_discoveredTests) |
Return IReadOnlyList |
TUnit.Engine/Helpers/DataUnwrapper.cs |
41 | .Select(p => p.Type).ToArray() |
Pre-sized array + loop |
TUnit.Engine/Extensions/TestExtensions.cs |
37, 46, 57, 208 | Multiple .Select().ToArray() chains |
Manual loops |
TUnit.Engine/Building/ReflectionMetadataBuilder.cs |
30-31, 78-79 | .Select().ToArray() for parameters |
Pre-sized array + loop |
TUnit.Engine/Discovery/ReflectionAttributeExtractor.cs |
125, 140, 164, 179, 192, 221 | Multiple .ToArray() calls |
Reuse lists or use ArrayPool |
Suggested Approach
Replace LINQ with pre-sized arrays:
// Before: LINQ allocation
var paramTypes = expectedParameters.Select(p => p.Type).ToArray();
// After: Pre-sized array
var paramTypes = new Type[expectedParameters.Length];
for (int i = 0; i < expectedParameters.Length; i++)
{
paramTypes[i] = expectedParameters[i].Type;
}Use ArrayPool for temporary buffers:
// Before
var tasks = new Task<List<TestMetadata>>[assemblies.Count];
// After
var tasks = ArrayPool<Task<List<TestMetadata>>>.Shared.Rent(assemblies.Count);
try
{
// ... use tasks
}
finally
{
ArrayPool<Task<List<TestMetadata>>>.Shared.Return(tasks);
}Avoid defensive copies:
// Before: Creates copy
return new List<TestMetadata>(_discoveredTests);
// After: Return read-only view
return _discoveredTests.AsReadOnly();Verification
- Use
dotnet-tracewith allocation tracking - Compare object allocation counts before/after at 10k scale
- Profile with SpeedScope to verify no new hot spots introduced
Risks
- Low-medium risk
ArrayPoolrequires careful return (use try/finally)- Removing defensive copies requires verifying callers don't mutate
Priority
P2 - Mixed complexity, broad impact across many files
Metadata
Metadata
Assignees
Labels
No labels