diff --git a/common/Perf/Azure.Test.Perf/PerfOptions.cs b/common/Perf/Azure.Test.Perf/PerfOptions.cs index dbd95980dac33..b4bfd66f6e062 100644 --- a/common/Perf/Azure.Test.Perf/PerfOptions.cs +++ b/common/Perf/Azure.Test.Perf/PerfOptions.cs @@ -25,6 +25,18 @@ public class PerfOptions [Option('l', "latency", HelpText = "Track and print per-operation latency statistics")] public bool Latency { get; set; } + [Option("max-io-completion-threads", HelpText = "The maximum number of asynchronous I/O threads that the thread pool creates on demand")] + public int? MaxIOCompletionThreads { get; set; } + + [Option("max-worker-threads", HelpText = "The maximum number of worker threads that the thread pool creates on demand")] + public int? MaxWorkerThreads { get; set; } + + [Option("min-io-completion-threads", HelpText = "The minimum number of asynchronous I/O threads that the thread pool creates on demand")] + public int? MinIOCompletionThreads { get; set; } + + [Option("min-worker-threads", HelpText = "The minimum number of worker threads that the thread pool creates on demand")] + public int? MinWorkerThreads { get; set; } + [Option("no-cleanup", HelpText = "Disables test cleanup")] public bool NoCleanup { get; set; } diff --git a/common/Perf/Azure.Test.Perf/PerfProgram.cs b/common/Perf/Azure.Test.Perf/PerfProgram.cs index ee00b4cb74177..2ef3d49943205 100644 --- a/common/Perf/Azure.Test.Perf/PerfProgram.cs +++ b/common/Perf/Azure.Test.Perf/PerfProgram.cs @@ -75,6 +75,10 @@ private static async Task Run(Type testType, PerfOptions options) })); Console.WriteLine(); + ConfigureThreadPool(options); + + PrintEnvironment(); + using var setupStatusCts = new CancellationTokenSource(); var setupStatusThread = PerfStressUtilities.PrintStatus("=== Setup ===", () => ".", newLine: false, setupStatusCts.Token); @@ -174,6 +178,52 @@ private static async Task Run(Type testType, PerfOptions options) PrintAssemblyVersions(testType); } + private static void ConfigureThreadPool(PerfOptions options) + { + if (options.MinWorkerThreads.HasValue || options.MinIOCompletionThreads.HasValue) + { + ThreadPool.GetMinThreads(out var minWorkerThreads, out var minIOCompletionThreads); + var successful = ThreadPool.SetMinThreads(options.MinWorkerThreads ?? minWorkerThreads, + options.MinIOCompletionThreads ?? minIOCompletionThreads); + + if (!successful) + { + throw new InvalidOperationException("ThreadPool.SetMinThreads() was unsuccessful"); + } + } + + if (options.MaxWorkerThreads.HasValue || options.MaxIOCompletionThreads.HasValue) + { + ThreadPool.GetMaxThreads(out var maxWorkerThreads, out var maxIOCompletionThreads); + var successful = ThreadPool.SetMaxThreads(options.MaxWorkerThreads ?? maxWorkerThreads, + options.MaxIOCompletionThreads ?? maxIOCompletionThreads); + + if (!successful) + { + throw new InvalidOperationException("ThreadPool.SetMaxThreads() was unsuccessful"); + } + } + } + + private static void PrintEnvironment() + { + Console.WriteLine("=== Environment ==="); + + Console.WriteLine($"GCSettings.IsServerGC: {GCSettings.IsServerGC}"); + + Console.WriteLine($"Environment.ProcessorCount: {Environment.ProcessorCount}"); + Console.WriteLine($"Environment.Is64BitProcess: {Environment.Is64BitProcess}"); + + ThreadPool.GetMinThreads(out var minWorkerThreads, out var minCompletionPortThreads); + ThreadPool.GetMaxThreads(out var maxWorkerThreads, out var maxCompletionPortThreads); + Console.WriteLine($"ThreadPool.MinWorkerThreads: {minWorkerThreads}"); + Console.WriteLine($"ThreadPool.MinCompletionPortThreads: {minCompletionPortThreads}"); + Console.WriteLine($"ThreadPool.MaxWorkerThreads: {maxWorkerThreads}"); + Console.WriteLine($"ThreadPool.MaxCompletionPortThreads: {maxCompletionPortThreads}"); + + Console.WriteLine(); + } + private static void PrintAssemblyVersions(Type testType) { Console.WriteLine("=== Versions ==="); @@ -186,10 +236,6 @@ private static void PrintAssemblyVersions(Type testType) // Include all Track1 and Track2 assemblies .Where(a => a.GetName().Name.StartsWith("Azure", StringComparison.OrdinalIgnoreCase) || a.GetName().Name.StartsWith("Microsoft.Azure", StringComparison.OrdinalIgnoreCase)) - // Exclude this perf framework - .Where(a => a != Assembly.GetExecutingAssembly()) - // Exclude assembly containing the perf test itself - .Where(a => a != testType.Assembly) // Exclude Azure.Core.TestFramework since it is only used to setup environment and should not impact results .Where(a => !a.GetName().Name.Equals("Azure.Core.TestFramework", StringComparison.OrdinalIgnoreCase)) .OrderBy(a => a.GetName().Name); @@ -200,11 +246,16 @@ private static void PrintAssemblyVersions(Type testType) var referencedVersion = referencedAssemblies.Where(r => r.Name == name).SingleOrDefault()?.Version; var loadedVersion = a.GetName().Version; var informationalVersion = FileVersionInfo.GetVersionInfo(a.Location).ProductVersion; + var debuggableAttribute = (DebuggableAttribute)(a.GetCustomAttribute(typeof(DebuggableAttribute))); Console.WriteLine($"{name}:"); - Console.WriteLine($" Referenced: {referencedVersion}"); + if (referencedVersion != null) + { + Console.WriteLine($" Referenced: {referencedVersion}"); + } Console.WriteLine($" Loaded: {loadedVersion}"); Console.WriteLine($" Informational: {informationalVersion}"); + Console.WriteLine($" JITOptimizer: {(debuggableAttribute.IsJITOptimizerDisabled ? "Disabled" : "Enabled")}"); } Console.WriteLine();