-
-
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
Several objects are allocated per-test and immediately discarded. Pooling these reduces GC pressure and improves cache locality.
Evidence
| File | Lines | Object | Frequency |
|---|---|---|---|
TUnit.Engine/TestExecutor.cs |
162 | List<Exception> |
Per-test |
TUnit.Engine/Services/HookExecutor.cs |
67, 129, 196, 288 | List<Exception> |
Per-hook-level (4x per test) |
TUnit.Engine/Building/TestBuilderPipeline.cs |
39 | ConcurrentDictionary<string, object?> state bags |
Per-test |
TUnit.Assertions/Sources/ValueAssertion.cs |
18-20 | StringBuilder for expressions |
Per-assertion |
TUnit.Engine/Discovery/ReflectionTestDataCollector.cs |
338 | List<TestMetadata>(100) |
Per-assembly |
Suggested Approach
Use ObjectPool<T> from Microsoft.Extensions.ObjectPool or a simple thread-static pattern:
// Thread-static pool for List<Exception>
internal static class ExceptionListPool
{
[ThreadStatic] private static List<Exception>? _cached;
public static List<Exception> Rent()
{
var list = _cached ?? new List<Exception>(4);
_cached = null;
return list;
}
public static void Return(List<Exception> list)
{
list.Clear();
_cached = list;
}
}For StringBuilder in assertions, consider lazy initialization - only create when assertion fails:
// Defer StringBuilder creation until failure
private StringBuilder? _expressionBuilder;
private StringBuilder ExpressionBuilder => _expressionBuilder ??= new StringBuilder();Verification
- Profile memory allocations before/after using
dotnet-tracewithgc-collectevents - Compare object allocation counts at 10k test scale
- Verify no memory leaks (pooled objects properly returned)
Risks
- Medium risk - must ensure pooled objects are always returned
- Thread-static pools have no cross-thread sharing (may need larger pools for high parallelism)
StringBuilderlazy init changes behavior slightly - ensure tests still pass
Priority
P1 - Medium complexity, high memory impact
Metadata
Metadata
Assignees
Labels
No labels