Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions TUnit.Engine/Scheduling/TestScheduler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -473,13 +473,14 @@ private async Task ExecuteUnlimitedAsync(
List<AbstractExecutableTest> tests,
CancellationToken cancellationToken)
{
// Execute tests without limiters with unlimited parallelism (no global semaphore overhead)
// Execute tests without limiters - still apply global parallelism limit to prevent thread pool saturation
// "Unlimited" means no per-test constraints (ParallelLimiter), not unlimited concurrency
await Parallel.ForEachAsync(
tests,
new ParallelOptions
{
MaxDegreeOfParallelism = _maxParallelism,
CancellationToken = cancellationToken
// No MaxDegreeOfParallelism = unlimited parallelism
},
async (test, ct) =>
{
Expand Down Expand Up @@ -550,10 +551,11 @@ private static int GetMaxParallelism(ILogger logger, ICommandLineOptions command
}
}

// Default: 8x CPU cores (optimized for I/O-bound and async tests)
// Allows higher concurrency for tests with async/await patterns while preventing resource exhaustion
var defaultLimit = Environment.ProcessorCount * 8;
logger.LogDebug($"Maximum parallel tests limit defaulting to {defaultLimit} ({Environment.ProcessorCount} processors * 8)");
// Default: 4x CPU cores (balanced for mixed CPU/I/O workloads)
// Higher values can cause thread pool saturation with many async tests
// Users can override via --maximum-parallel-tests or TUNIT_MAX_PARALLEL_TESTS
var defaultLimit = Environment.ProcessorCount * 4;
logger.LogDebug($"Maximum parallel tests limit defaulting to {defaultLimit} ({Environment.ProcessorCount} processors * 4)");
return defaultLimit;
}
}
75 changes: 75 additions & 0 deletions TUnit.Profile/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -156,3 +156,78 @@ public class MixedWorkloadTests
[Repeat(30)]
public async Task QuickAsyncTest3() { await Task.CompletedTask; _ = 3; }
}

/// <summary>
/// Massive parallel tests similar to benchmark suite - stress tests parallelism
/// with large numbers of CPU-bound, IO-bound, and mixed workload tests
/// </summary>
public class MassiveParallelTests
{
[Test]
[Arguments(1)] [Arguments(2)] [Arguments(3)] [Arguments(4)] [Arguments(5)]
[Arguments(6)] [Arguments(7)] [Arguments(8)] [Arguments(9)] [Arguments(10)]
[Arguments(11)] [Arguments(12)] [Arguments(13)] [Arguments(14)] [Arguments(15)]
[Arguments(16)] [Arguments(17)] [Arguments(18)] [Arguments(19)] [Arguments(20)]
[Arguments(21)] [Arguments(22)] [Arguments(23)] [Arguments(24)] [Arguments(25)]
[Arguments(26)] [Arguments(27)] [Arguments(28)] [Arguments(29)] [Arguments(30)]
[Arguments(31)] [Arguments(32)] [Arguments(33)] [Arguments(34)] [Arguments(35)]
[Arguments(36)] [Arguments(37)] [Arguments(38)] [Arguments(39)] [Arguments(40)]
[Arguments(41)] [Arguments(42)] [Arguments(43)] [Arguments(44)] [Arguments(45)]
[Arguments(46)] [Arguments(47)] [Arguments(48)] [Arguments(49)]
public void Parallel_CPUBound_Test(int taskId)
{
var result = PerformCPUWork(taskId);
var doubled = result * 2;
_ = doubled;
}

[Test]
[Arguments(1)] [Arguments(2)] [Arguments(3)] [Arguments(4)] [Arguments(5)]
[Arguments(6)] [Arguments(7)] [Arguments(8)] [Arguments(9)] [Arguments(10)]
[Arguments(11)] [Arguments(12)] [Arguments(13)] [Arguments(14)] [Arguments(15)]
[Arguments(16)] [Arguments(17)] [Arguments(18)] [Arguments(19)] [Arguments(20)]
[Arguments(21)] [Arguments(22)] [Arguments(23)] [Arguments(24)] [Arguments(25)]
[Arguments(26)] [Arguments(27)] [Arguments(28)] [Arguments(29)] [Arguments(30)]
[Arguments(31)] [Arguments(32)] [Arguments(33)] [Arguments(34)] [Arguments(35)]
[Arguments(36)] [Arguments(37)] [Arguments(38)] [Arguments(39)] [Arguments(40)]
[Arguments(41)] [Arguments(42)] [Arguments(43)] [Arguments(44)] [Arguments(45)]
[Arguments(46)] [Arguments(47)] [Arguments(48)] [Arguments(49)]
public async Task Parallel_IOBound_Test(int taskId)
{
await Task.Delay(50);
var result = await PerformIOWorkAsync(taskId);
var length = result.Length;
_ = length;
}

[Test]
[Arguments(1, 101)] [Arguments(2, 102)] [Arguments(3, 103)] [Arguments(4, 104)]
[Arguments(5, 105)] [Arguments(6, 106)] [Arguments(7, 107)] [Arguments(8, 108)]
[Arguments(9, 109)] [Arguments(10, 110)] [Arguments(11, 111)] [Arguments(12, 112)]
[Arguments(13, 113)] [Arguments(14, 114)] [Arguments(15, 115)] [Arguments(16, 116)]
[Arguments(17, 117)] [Arguments(18, 118)] [Arguments(19, 119)]
public async Task Parallel_Mixed_Test(int cpuValue, int ioValue)
{
var cpuResult = PerformCPUWork(cpuValue);
await Task.Yield();
var ioResult = await PerformIOWorkAsync(ioValue);
var combined = $"{cpuResult}_{ioResult}";
_ = combined;
}

private static int PerformCPUWork(int taskId)
{
var sum = 0;
for (var i = 1; i <= 100; i++)
{
sum += (i * taskId) % 1000;
}
return sum;
}

private static async Task<string> PerformIOWorkAsync(int taskId)
{
await Task.Yield();
return $"Task_{taskId}_Result_{Guid.NewGuid().ToString()[..8]}";
}
}
Loading