public static async Task MeasureFirstRequest(string[] args) { var url = args.FirstOrDefault(arg => arg.StartsWith("http", StringComparison.OrdinalIgnoreCase)); if (url == null) { Console.WriteLine("URL not found, skipping first request"); return; } var cts = new CancellationTokenSource(30000); var httpMessage = new HttpRequestMessage(HttpMethod.Get, url); var stopwatch = new Stopwatch(); stopwatch.Start(); try { using (var response = await _httpClient.SendAsync(httpMessage, cts.Token)) { var elapsed = stopwatch.ElapsedMilliseconds; Console.WriteLine($"{elapsed} ms"); BenchmarksEventSource.Register("http/firstrequest", Operations.Max, Operations.Max, "First Request (ms)", "Time to first request in ms", "n0"); BenchmarksEventSource.Measure("http/firstrequest", elapsed); } } catch (OperationCanceledException) { Console.WriteLine("A timeout occurred while measuring the first request"); } }
static void Main(string[] args) { #if RUNNING_CRANK Console.WriteLine("Application started."); Stopwatch sw = new(); sw.Start(); #endif SerializationMechanism.RunBenchmark(); #if RUNNING_CRANK sw.Stop(); Process process = Process.GetCurrentProcess(); process.Refresh(); //Console.WriteLine(process.PrivateMemorySize64 / 1024); //Console.WriteLine(sw.ElapsedMilliseconds); BenchmarksEventSource.Register("runtime/private-bytes", Operations.First, Operations.First, "Private bytes (KB)", "Private bytes (KB)", "n0"); BenchmarksEventSource.Measure("runtime/private-bytes", process.PrivateMemorySize64 / 1024); BenchmarksEventSource.Register("application/elapsed-time", Operations.First, Operations.First, "Elapsed time (ms)", "Elasped time (ms)", "n0"); BenchmarksEventSource.Measure("application/elapsed-time", sw.ElapsedMilliseconds); //Thread.Sleep(2000); #endif }
public async Task Run(ClientParameters clientParams, LoadGeneratorParameters loadParams) { var secrets = SecretConfiguration.Load(clientParams.SecretSource); var hostBuilder = new HostBuilder().UseOrleansClient((ctx, builder) => builder.Configure <ClusterOptions>(options => { options.ClusterId = clientParams.ClusterId; options.ServiceId = clientParams.ServiceId; }) .Configure <ConnectionOptions>(options => clientParams.ConnectionsPerEndpoint = 2) .UseAzureStorageClustering(options => options.ConfigureTableServiceClient(secrets.ClusteringConnectionString))); using var host = hostBuilder.Build(); _logger.LogInformation("Connecting to cluster..."); await host.StartAsync(); var client = host.Services.GetService <IClusterClient>(); var generator = new ConcurrentLoadGenerator <T>( numWorkers: loadParams.NumWorkers, blocksPerWorker: loadParams.BlocksPerWorker != 0 ? loadParams.BlocksPerWorker : int.MaxValue, requestsPerBlock: loadParams.RequestsPerBlock, issueRequest: _scenario.IssueRequest, getStateForWorker: workerId => _scenario.GetStateForWorker(client, workerId), logger: _logger, logIntermediateResults: true); _logger.LogInformation("Warming-up..."); await generator.Warmup(); var cts = loadParams.Duration != 0 ? new CancellationTokenSource(TimeSpan.FromSeconds(loadParams.Duration)) : new CancellationTokenSource(); _logger.LogInformation("Running"); var report = await generator.Run(cts.Token); // Register the measurements. n0 -> format as natural number BenchmarksEventSource.Register("requests", Operations.First, Operations.Sum, "Requests", "Number of requests completed", "n0"); BenchmarksEventSource.Register("failures", Operations.First, Operations.Sum, "Failures", "Number of failures", "n0"); BenchmarksEventSource.Register("rps", Operations.First, Operations.Sum, "Rate per second", "Rate per seconds", "n0"); // Register the measurement values BenchmarksEventSource.Measure("requests", report.Completed); BenchmarksEventSource.Measure("failures", report.Failures); BenchmarksEventSource.Measure("rps", report.RatePerSecond); await host.StopAsync(); }
private async Task RunAsync(Parameters parameters) { _logger.LogInformation("Connecting to cluster..."); var secrets = SecretConfiguration.Load(parameters.SecretSource); var hostBuilder = new HostBuilder() .UseOrleansClient(builder => { builder .Configure <ClusterOptions>(options => { options.ClusterId = parameters.ClusterId; options.ServiceId = parameters.ServiceId; }) .UseAzureStorageClustering(options => options.ConfigureTableServiceClient(secrets.ClusteringConnectionString)); }); using var host = hostBuilder.Build(); await host.StartAsync(); var client = host.Services.GetService <IClusterClient>(); var counterGrain = client.GetGrain <ICounterGrain>(parameters.CounterKey); var duration = await counterGrain.GetRunDuration(); BenchmarksEventSource.Register("duration", Operations.First, Operations.Last, "duration", "duration", "n0"); BenchmarksEventSource.Measure("duration", duration.TotalSeconds); var initialWait = await counterGrain.WaitTimeForReport(); _logger.LogInformation($"Counters should be ready in {initialWait}"); await Task.Delay(initialWait); _logger.LogInformation($"Counters ready"); foreach (var counter in parameters.Counters) { var value = await counterGrain.GetTotalCounterValue(counter); _logger.LogInformation($"{counter}: {value}"); BenchmarksEventSource.Register(counter, Operations.First, Operations.Sum, counter, counter, "n0"); BenchmarksEventSource.Measure(counter, value); if (string.Equals(counter, "requests", StringComparison.InvariantCultureIgnoreCase)) { var rps = (float)value / duration.TotalSeconds; BenchmarksEventSource.Register("rps", Operations.First, Operations.Last, "rps", "Requests per second", "n0"); BenchmarksEventSource.Measure("rps", rps); } } await host.StopAsync(); }
private static void ParseOutput() { BenchmarksEventSource.Register("h2load/requests;http/requests", Operations.Max, Operations.Sum, "Requests", "Total number of requests", "n0"); BenchmarksEventSource.Register("h2load/badresponses;http/requests/badresponses", Operations.Max, Operations.Sum, "Bad responses", "Non-2xx or 3xx responses", "n0"); BenchmarksEventSource.Register("h2load/errors/socketerrors;http/requests/errors", Operations.Max, Operations.Sum, "Socket errors", "Socket errors", "n0"); BenchmarksEventSource.Register("h2load/latency/mean;http/latency/mean", Operations.Max, Operations.Sum, "Mean latency (ms)", "Mean latency (ms)", "n2"); BenchmarksEventSource.Register("h2load/latency/max;http/latency/max", Operations.Max, Operations.Sum, "Max latency (ms)", "Max latency (ms)", "n2"); BenchmarksEventSource.Register("h2load/rps/max;http/rps/max", Operations.Max, Operations.Sum, "Max RPS", "RPS: max", "n0"); BenchmarksEventSource.Register("h2load/raw", Operations.All, Operations.All, "Raw results", "Raw results", "object"); double rps = 0; var rpsMatch = Regex.Match(Output, @"([\d\.]+) req/s"); if (rpsMatch.Success && rpsMatch.Groups.Count == 2) { rps = double.Parse(rpsMatch.Groups[1].Value); } var latencyMatch = Regex.Match(Output, @"time for request: \s+[\d\.]+\w+\s+[\d\.]+\w+\s+([\d\.]+)(\w+)"); var averageLatency = ReadLatency(latencyMatch); var p100Match = Regex.Match(Output, @"time for request: \s+[\d\.]+\w+\s+([\d\.]+)(\w+)"); var maxLatency = ReadLatency(p100Match); var socketErrorsMatch = Regex.Match(Output, @"([\d\.]+) failed, ([\d\.]+) errored, ([\d\.]+) timeout"); var socketErrors = CountSocketErrors(socketErrorsMatch); var badResponsesMatch = Regex.Match(Output, @"status codes: ([\d\.]+) 2xx, ([\d\.]+) 3xx, ([\d\.]+) 4xx, ([\d\.]+) 5xx"); var badResponses = ReadBadResponses(badResponsesMatch); var requestsCountMatch = Regex.Match(Output, @"requests: ([\d\.]+) total"); var totalRequests = ReadRequests(requestsCountMatch); BenchmarksEventSource.Measure("h2load/requests;http/requests", totalRequests); BenchmarksEventSource.Measure("h2load/badresponses;http/requests/badresponses", badResponses); BenchmarksEventSource.Measure("h2load/errors/socketerrors;http/requests/errors", socketErrors); BenchmarksEventSource.Measure("h2load/latency/mean;http/latency/mean", averageLatency); BenchmarksEventSource.Measure("h2load/latency/max;http/latency/max", maxLatency); BenchmarksEventSource.Measure("h2load/rps/max;http/rps/max", rps); BenchmarksEventSource.Measure("h2load/raw", Output); }
public static async Task MeasureFirstRequest(string[] args) { var url = args.FirstOrDefault(arg => arg.StartsWith("http", StringComparison.OrdinalIgnoreCase)); if (url == null) { Console.WriteLine("URL not found, skipping first request"); return; } // Configuring the http client to trust the self-signed certificate var httpClientHandler = new HttpClientHandler(); httpClientHandler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator; httpClientHandler.MaxConnectionsPerServer = 1; using (var httpClient = new HttpClient(httpClientHandler)) { var cts = new CancellationTokenSource(5000); var httpMessage = new HttpRequestMessage(HttpMethod.Get, url); var stopwatch = new Stopwatch(); stopwatch.Start(); try { using (var response = await httpClient.SendAsync(httpMessage, cts.Token)) { var elapsed = stopwatch.ElapsedMilliseconds; Console.WriteLine($"{elapsed} ms"); BenchmarksEventSource.Register("http/firstrequest", Operations.Max, Operations.Max, "First Request (ms)", "Time to first request in ms", "n0"); BenchmarksEventSource.Measure("http/firstrequest", elapsed); } } catch (OperationCanceledException) { Console.WriteLine("A timeout occurred while measuring the first request"); } } }
static async Task <int> Main(string[] args) { Console.WriteLine("Bombardier Client"); Console.WriteLine("args: " + String.Join(' ', args)); Console.Write("Measuring first request ... "); await MeasureFirstRequest(args); // Extracting parameters var argsList = args.ToList(); TryGetArgumentValue("-w", argsList, out int warmup); TryGetArgumentValue("-d", argsList, out int duration); TryGetArgumentValue("-n", argsList, out int requests); TryGetArgumentValue("-o", argsList, out string outputFormat); TryGetArgumentValue("-f", argsList, out string bodyFile); if (duration == 0 && requests == 0) { Console.WriteLine("Couldn't find valid -d and -n arguments (integers)"); return(-1); } if (string.IsNullOrEmpty(outputFormat)) { outputFormat = "json"; } else if ((outputFormat != "json") && (outputFormat != "plain-text")) { Console.WriteLine("Value value for -o is json or plain-text"); return(-1); } args = argsList.ToArray(); string bombardierUrl = null; string bombardierVersion = "1.2.5"; if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { bombardierUrl = "https://github.com/codesenberg/bombardier/releases/download/v1.2.5/bombardier-windows-amd64.exe"; } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { switch (RuntimeInformation.ProcessArchitecture) { case Architecture.Arm64: bombardierUrl = "https://github.com/codesenberg/bombardier/releases/download/v1.2.5/bombardier-linux-arm64"; break; case Architecture.Arm: bombardierUrl = "https://github.com/codesenberg/bombardier/releases/download/v1.2.5/bombardier-linux-arm"; break; default: bombardierUrl = "https://github.com/codesenberg/bombardier/releases/download/v1.2.5/bombardier-linux-amd64"; break; } } else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { bombardierUrl = "https://github.com/codesenberg/bombardier/releases/download/v1.2.5/bombardier-darwin-amd64"; } else { Console.WriteLine("Unsupported platform"); return(-1); } var bombardierFileName = Path.Combine(Path.GetTempPath(), ".crank", bombardierVersion, Path.GetFileName(bombardierUrl)); if (!File.Exists(bombardierFileName)) { Directory.CreateDirectory(Path.GetDirectoryName(bombardierFileName)); Console.WriteLine($"Downloading bombardier from {bombardierUrl} to {bombardierFileName}"); using (var downloadStream = await _httpClient.GetStreamAsync(bombardierUrl)) using (var fileStream = File.Create(bombardierFileName)) { await downloadStream.CopyToAsync(fileStream); await fileStream.FlushAsync(); await downloadStream.FlushAsync(); } } if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { Console.WriteLine($"Setting execute permission on executable {bombardierFileName}"); Process.Start("chmod", "+x " + bombardierFileName); } if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { Console.WriteLine($"Allow running bombardier"); Process.Start("spctl", "--add " + bombardierFileName); } if (!String.IsNullOrEmpty(bodyFile)) { if (bodyFile.StartsWith("http", StringComparison.OrdinalIgnoreCase)) { Console.WriteLine($"Downloading body file {bodyFile}"); var tempFile = Path.GetTempFileName(); using (var downloadStream = await _httpClient.GetStreamAsync(bodyFile)) using (var fileStream = File.Create(tempFile)) { await downloadStream.CopyToAsync(fileStream); } bodyFile = tempFile; } argsList.Add("-f"); argsList.Add(bodyFile); } args = argsList.Select(Quote).ToArray(); var baseArguments = String.Join(' ', args) + $" --print r --format {outputFormat}"; var process = new Process() { StartInfo = { FileName = bombardierFileName, RedirectStandardOutput = true, UseShellExecute = false, }, EnableRaisingEvents = true }; var stringBuilder = new StringBuilder(); process.OutputDataReceived += (_, e) => { if (e != null && e.Data != null) { Console.WriteLine(e.Data); lock (stringBuilder) { stringBuilder.AppendLine(e.Data); } } }; // Warmup if (warmup > 0) { process.StartInfo.Arguments = $" -d {warmup}s {baseArguments}"; Console.WriteLine("> bombardier " + process.StartInfo.Arguments); await StartProcessWithRetriesAsync(process); process.WaitForExit(); if (process.ExitCode != 0) { Console.WriteLine("Failed to run bombardier."); return(process.ExitCode); } } else { Console.WriteLine("Warmup skipped"); } lock (stringBuilder) { stringBuilder.Clear(); } if (duration > 0) { process.StartInfo.Arguments = requests > 0 ? $" -n {requests} {baseArguments}" : $" -d {duration}s {baseArguments}"; Console.WriteLine("> bombardier " + process.StartInfo.Arguments); await StartProcessWithRetriesAsync(process); BenchmarksEventSource.SetChildProcessId(process.Id); process.BeginOutputReadLine(); process.WaitForExit(); if (process.ExitCode != 0) { return(process.ExitCode); } string output; lock (stringBuilder) { output = stringBuilder.ToString(); } if (outputFormat != "json") { return(0); } var document = JObject.Parse(output); BenchmarksEventSource.Register("bombardier/requests;http/requests", Operations.Max, Operations.Sum, "Requests", "Total number of requests", "n0"); BenchmarksEventSource.Register("bombardier/badresponses;http/requests/badresponses", Operations.Max, Operations.Sum, "Bad responses", "Non-2xx or 3xx responses", "n0"); BenchmarksEventSource.Register("bombardier/latency/50;http/latency/50", Operations.Max, Operations.Max, "Latency 50th (ms)", "Latency 50th (ms)", "n2"); BenchmarksEventSource.Register("bombardier/latency/75;http/latency/75", Operations.Max, Operations.Max, "Latency 75th (ms)", "Latency 75th (ms)", "n2"); BenchmarksEventSource.Register("bombardier/latency/90;http/latency/90", Operations.Max, Operations.Max, "Latency 90th (ms)", "Latency 90th (ms)", "n2"); BenchmarksEventSource.Register("bombardier/latency/95;http/latency/95", Operations.Max, Operations.Max, "Latency 95th (ms)", "Latency 95th (ms)", "n2"); BenchmarksEventSource.Register("bombardier/latency/99;http/latency/99", Operations.Max, Operations.Max, "Latency 99th (ms)", "Latency 99th (ms)", "n2"); BenchmarksEventSource.Register("bombardier/latency/mean;http/latency/mean", Operations.Max, Operations.Avg, "Mean latency (ms)", "Mean latency (ms)", "n2"); BenchmarksEventSource.Register("bombardier/latency/max;http/latency/max", Operations.Max, Operations.Max, "Max latency (ms)", "Max latency (ms)", "n2"); BenchmarksEventSource.Register("bombardier/rps/mean;http/rps/mean", Operations.Max, Operations.Sum, "Requests/sec", "Requests per second", "n0"); BenchmarksEventSource.Register("bombardier/rps/max;http/rps/max", Operations.Max, Operations.Sum, "Requests/sec (max)", "Max requests per second", "n0"); BenchmarksEventSource.Register("bombardier/throughput;http/throughput", Operations.Max, Operations.Sum, "Read throughput (MB/s)", "Read throughput (MB/s)", "n2"); BenchmarksEventSource.Register("bombardier/raw", Operations.All, Operations.All, "Raw results", "Raw results", "json"); var total = document["result"]["req1xx"].Value <long>() + document["result"]["req2xx"].Value <long>() + document["result"]["req3xx"].Value <long>() + document["result"]["req4xx"].Value <long>() + document["result"]["req5xx"].Value <long>() + document["result"]["others"].Value <long>(); var success = document["result"]["req2xx"].Value <long>() + document["result"]["req3xx"].Value <long>(); BenchmarksEventSource.Measure("bombardier/requests;http/requests", total); BenchmarksEventSource.Measure("bombardier/badresponses;http/requests/badresponses", total - success); BenchmarksEventSource.Measure("bombardier/latency/50;http/latency/50", document["result"]["latency"]["percentiles"]["50"].Value <double>().ToMilliseconds()); BenchmarksEventSource.Measure("bombardier/latency/75;http/latency/75", document["result"]["latency"]["percentiles"]["75"].Value <double>().ToMilliseconds()); BenchmarksEventSource.Measure("bombardier/latency/90;http/latency/90", document["result"]["latency"]["percentiles"]["90"].Value <double>().ToMilliseconds()); BenchmarksEventSource.Measure("bombardier/latency/95;http/latency/95", document["result"]["latency"]["percentiles"]["95"].Value <double>().ToMilliseconds()); BenchmarksEventSource.Measure("bombardier/latency/99;http/latency/99", document["result"]["latency"]["percentiles"]["99"].Value <double>().ToMilliseconds()); BenchmarksEventSource.Measure("bombardier/latency/mean;http/latency/mean", document["result"]["latency"]["mean"].Value <double>().ToMilliseconds()); BenchmarksEventSource.Measure("bombardier/latency/max;http/latency/max", document["result"]["latency"]["max"].Value <double>().ToMilliseconds()); BenchmarksEventSource.Measure("bombardier/rps/max;http/rps/max", document["result"]["rps"]["max"].Value <double>()); BenchmarksEventSource.Measure("bombardier/rps/mean;http/rps/mean", document["result"]["rps"]["mean"].Value <double>()); BenchmarksEventSource.Measure("bombardier/raw", output); var bytesPerSecond = document["result"]["bytesRead"].Value <long>() / document["result"]["timeTakenSeconds"].Value <double>(); // B/s to MB/s BenchmarksEventSource.Measure("bombardier/throughput;http/throughput", bytesPerSecond / 1024 / 1024); } else { Console.WriteLine("Benchmark skipped"); } // Clean temporary files try { File.Delete(bodyFile); } catch { } return(0); }
static int RunCore(string fileName, string[] args, bool parseLatency) { // Extracting duration parameters string warmup = ""; string duration = ""; var argsList = args.ToList(); var durationIndex = argsList.FindIndex(x => String.Equals(x, "-d", StringComparison.OrdinalIgnoreCase)); if (durationIndex >= 0) { duration = argsList[durationIndex + 1]; argsList.RemoveAt(durationIndex); argsList.RemoveAt(durationIndex); } else { Console.WriteLine("Couldn't find -d argument"); return(-1); } var warmupIndex = argsList.FindIndex(x => String.Equals(x, "-w", StringComparison.OrdinalIgnoreCase)); if (warmupIndex >= 0) { warmup = argsList[warmupIndex + 1]; argsList.RemoveAt(warmupIndex); argsList.RemoveAt(warmupIndex); } args = argsList.Select(Quote).ToArray(); var baseArguments = String.Join(' ', args); var process = new Process() { StartInfo = { FileName = fileName, RedirectStandardOutput = true, UseShellExecute = false, }, EnableRaisingEvents = true }; var stringBuilder = new StringBuilder(); process.OutputDataReceived += (_, e) => { if (e != null && e.Data != null) { Console.WriteLine(e.Data); lock (stringBuilder) { stringBuilder.AppendLine(e.Data); } } }; // Warmup if (!String.IsNullOrEmpty(warmup) && warmup != "0s") { process.StartInfo.Arguments = $"-d {warmup} {baseArguments}"; Console.WriteLine("> wrk " + process.StartInfo.Arguments); process.Start(); process.WaitForExit(); if (process.ExitCode != 0) { return(process.ExitCode); } } lock (stringBuilder) { stringBuilder.Clear(); } process.StartInfo.Arguments = $"-d {duration} {baseArguments}"; Console.WriteLine("> wrk " + process.StartInfo.Arguments); process.Start(); BenchmarksEventSource.SetChildProcessId(process.Id); process.BeginOutputReadLine(); process.WaitForExit(); if (process.ExitCode != 0) { return(process.ExitCode); } string output; lock (stringBuilder) { output = stringBuilder.ToString(); } BenchmarksEventSource.Register("wrk/rps/mean", Operations.Max, Operations.Sum, "Requests/sec", "Requests per second", "n0"); BenchmarksEventSource.Register("wrk/requests", Operations.Max, Operations.Sum, "Requests", "Total number of requests", "n0"); BenchmarksEventSource.Register("wrk/latency/mean", Operations.Max, Operations.Sum, "Mean latency (ms)", "Mean latency (ms)", "n2"); BenchmarksEventSource.Register("wrk/latency/max", Operations.Max, Operations.Sum, "Max latency (ms)", "Max latency (ms)", "n2"); BenchmarksEventSource.Register("wrk/errors/badresponses", Operations.Max, Operations.Sum, "Bad responses", "Non-2xx or 3xx responses", "n0"); BenchmarksEventSource.Register("wrk/errors/socketerrors", Operations.Max, Operations.Sum, "Socket errors", "Socket errors", "n0"); BenchmarksEventSource.Register("wrk/throughput", Operations.Max, Operations.Sum, "Read throughput (MB/s)", "Read throughput (MB/s)", "n2"); const string LatencyPattern = @"\s+{0}\s+([\d\.]+)(\w+)"; var latencyMatch = Regex.Match(output, String.Format(LatencyPattern, "Latency")); BenchmarksEventSource.Measure("wrk/latency/mean", ReadLatency(latencyMatch)); var rpsMatch = Regex.Match(output, @"Requests/sec:\s*([\d\.]*)"); if (rpsMatch.Success && rpsMatch.Groups.Count == 2) { BenchmarksEventSource.Measure("wrk/rps/mean", double.Parse(rpsMatch.Groups[1].Value)); } var throughputMatch = Regex.Match(output, @"Transfer/sec:\s+([\d\.]+)(\w+)"); BenchmarksEventSource.Measure("wrk/throughput", ReadThroughput(throughputMatch)); // Max latency is 3rd number after "Latency " var maxLatencyMatch = Regex.Match(output, @"\s+Latency\s+[\d\.]+\w+\s+[\d\.]+\w+\s+([\d\.]+)(\w+)"); BenchmarksEventSource.Measure("wrk/latency/max", ReadLatency(maxLatencyMatch)); var requestsCountMatch = Regex.Match(output, @"([\d\.]*) requests in ([\d\.]*)(\w*)"); BenchmarksEventSource.Measure("wrk/requests", ReadRequests(requestsCountMatch)); var badResponsesMatch = Regex.Match(output, @"Non-2xx or 3xx responses: ([\d\.]*)"); BenchmarksEventSource.Measure("wrk/errors/badresponses", ReadBadReponses(badResponsesMatch)); var socketErrorsMatch = Regex.Match(output, @"Socket errors: connect ([\d\.]*), read ([\d\.]*), write ([\d\.]*), timeout ([\d\.]*)"); BenchmarksEventSource.Measure("wrk/errors/socketerrors", CountSocketErrors(socketErrorsMatch)); if (parseLatency) { BenchmarksEventSource.Register("wrk/latency/50", Operations.Max, Operations.Avg, "Latency 50th (ms)", "Latency 50th (ms)", "n2"); BenchmarksEventSource.Register("wrk/latency/75", Operations.Max, Operations.Avg, "Latency 75th (ms)", "Latency 50th (ms)", "n2"); BenchmarksEventSource.Register("wrk/latency/90", Operations.Max, Operations.Avg, "Latency 90th (ms)", "Latency 50th (ms)", "n2"); BenchmarksEventSource.Register("wrk/latency/99", Operations.Max, Operations.Avg, "Latency 99th (ms)", "Latency 50th (ms)", "n2"); BenchmarksEventSource.Measure("wrk/latency/50", ReadLatency(Regex.Match(output, string.Format(LatencyPattern, "50%")))); BenchmarksEventSource.Measure("wrk/latency/75", ReadLatency(Regex.Match(output, string.Format(LatencyPattern, "75%")))); BenchmarksEventSource.Measure("wrk/latency/90", ReadLatency(Regex.Match(output, string.Format(LatencyPattern, "90%")))); BenchmarksEventSource.Measure("wrk/latency/99", ReadLatency(Regex.Match(output, string.Format(LatencyPattern, "99%")))); } return(0); }
static async Task <int> Main(string[] args) { Console.WriteLine("WRK2 Client"); Console.WriteLine("args: " + String.Join(' ', args)); var wrk2Filename = await DownloadWrk2Async(); Console.Write("Measuring first request ... "); await MeasureFirstRequest(args); // Do we need to parse latency? var parseLatency = args.Any(x => x == "--latency" || x == "-L"); // Extracting duration parameters string warmup = ""; string duration = ""; var argsList = args.ToList(); var durationIndex = argsList.FindIndex(x => String.Equals(x, "-d", StringComparison.OrdinalIgnoreCase)); if (durationIndex >= 0) { duration = argsList[durationIndex + 1]; argsList.RemoveAt(durationIndex); argsList.RemoveAt(durationIndex); } else { Console.WriteLine("Couldn't find -d argument"); return(-1); } var warmupIndex = argsList.FindIndex(x => String.Equals(x, "-w", StringComparison.OrdinalIgnoreCase)); if (warmupIndex >= 0) { warmup = argsList[warmupIndex + 1]; argsList.RemoveAt(warmupIndex); argsList.RemoveAt(warmupIndex); } args = argsList.Select(Quote).ToArray(); var baseArguments = String.Join(' ', args) + " --print r --format json"; var process = new Process() { StartInfo = { FileName = wrk2Filename, RedirectStandardOutput = true, UseShellExecute = false, }, EnableRaisingEvents = true }; var stringBuilder = new StringBuilder(); process.OutputDataReceived += (_, e) => { if (e != null && e.Data != null) { Console.WriteLine(e.Data); lock (stringBuilder) { stringBuilder.AppendLine(e.Data); } } }; // Warmup if (!String.IsNullOrEmpty(warmup) && warmup != "0s") { process.StartInfo.Arguments = $" -d {warmup} {baseArguments}"; Console.WriteLine("> wrk2 " + process.StartInfo.Arguments); process.Start(); process.WaitForExit(); if (process.ExitCode != 0) { return(process.ExitCode); } } lock (stringBuilder) { stringBuilder.Clear(); } process.StartInfo.Arguments = $" -d {duration} {baseArguments}"; Console.WriteLine("> wrk2 " + process.StartInfo.Arguments); process.Start(); BenchmarksEventSource.SetChildProcessId(process.Id); process.BeginOutputReadLine(); process.WaitForExit(); if (process.ExitCode != 0) { return(process.ExitCode); } string output; lock (stringBuilder) { output = stringBuilder.ToString(); } BenchmarksEventSource.Register("wrk2/rps/mean", Operations.Max, Operations.Sum, "Requests/sec", "Requests per second", "n0"); BenchmarksEventSource.Register("wrk2/requests", Operations.Max, Operations.Sum, "Requests", "Total number of requests", "n0"); BenchmarksEventSource.Register("wrk2/latency/mean", Operations.Max, Operations.Sum, "Mean latency (ms)", "Mean latency (ms)", "n2"); BenchmarksEventSource.Register("wrk2/latency/max", Operations.Max, Operations.Sum, "Max latency (ms)", "Max latency (ms)", "n2"); BenchmarksEventSource.Register("wrk2/errors/badresponses", Operations.Max, Operations.Sum, "Bad responses", "Non-2xx or 3xx responses", "n0"); BenchmarksEventSource.Register("wrk2/errors/socketerrors", Operations.Max, Operations.Sum, "Socket errors", "Socket errors", "n0"); var rpsMatch = Regex.Match(output, @"Requests/sec:\s*([\d\.]*)"); if (rpsMatch.Success && rpsMatch.Groups.Count == 2) { BenchmarksEventSource.Measure("wrk2/rps/mean", double.Parse(rpsMatch.Groups[1].Value)); } const string LatencyPattern = @"\s+{0}\s*([\d\.]+)([a-z]+)"; var avgLatencyMatch = Regex.Match(output, String.Format(LatencyPattern, "Latency")); BenchmarksEventSource.Measure("wrk2/latency/mean", ReadLatency(avgLatencyMatch)); // Max latency is 3rd number after "Latency " var maxLatencyMatch = Regex.Match(output, @"\s+Latency\s+[\d\.]+\w+\s+[\d\.]+\w+\s+([\d\.]+)(\w+)"); BenchmarksEventSource.Measure("wrk2/latency/max", ReadLatency(maxLatencyMatch)); var requestsCountMatch = Regex.Match(output, @"([\d\.]*) requests in ([\d\.]*)(\w*)"); BenchmarksEventSource.Measure("wrk2/requests", ReadRequests(requestsCountMatch)); var badResponsesMatch = Regex.Match(output, @"Non-2xx or 3xx responses: ([\d\.]*)"); BenchmarksEventSource.Measure("wrk2/errors/badresponses", ReadBadReponses(badResponsesMatch)); var socketErrorsMatch = Regex.Match(output, @"Socket errors: connect ([\d\.]*), read ([\d\.]*), write ([\d\.]*), timeout ([\d\.]*)"); BenchmarksEventSource.Measure("wrk2/errors/socketerrors", CountSocketErrors(socketErrorsMatch)); if (parseLatency) { BenchmarksEventSource.Register("wrk2/latency/50", Operations.Max, Operations.Avg, "Latency 50th (ms)", "Latency 50th (ms)", "n2"); BenchmarksEventSource.Register("wrk2/latency/75", Operations.Max, Operations.Avg, "Latency 75th (ms)", "Latency 50th (ms)", "n2"); BenchmarksEventSource.Register("wrk2/latency/90", Operations.Max, Operations.Avg, "Latency 90th (ms)", "Latency 50th (ms)", "n2"); BenchmarksEventSource.Register("wrk2/latency/99", Operations.Max, Operations.Avg, "Latency 99th (ms)", "Latency 50th (ms)", "n2"); BenchmarksEventSource.Register("wrk2/latency/99.9", Operations.Max, Operations.Avg, "Latency 99.9th (ms)", "Latency 50th (ms)", "n2"); BenchmarksEventSource.Register("wrk2/latency/99.99", Operations.Max, Operations.Avg, "Latency 99.99th (ms)", "Latency 50th (ms)", "n2"); BenchmarksEventSource.Register("wrk2/latency/99.999", Operations.Max, Operations.Avg, "Latency 99.999th (ms)", "Latency 50th (ms)", "n2"); BenchmarksEventSource.Register("wrk2/latency/100", Operations.Max, Operations.Avg, "Latency 100th (ms)", "Latency 50th (ms)", "n2"); BenchmarksEventSource.Register("wrk2/latency/distribution", Operations.All, Operations.All, "Latency distribution", "Latency distribution", "json"); BenchmarksEventSource.Measure("wrk2/latency/50", ReadLatency(Regex.Match(output, String.Format(LatencyPattern, "50\\.000%")))); BenchmarksEventSource.Measure("wrk2/latency/75", ReadLatency(Regex.Match(output, String.Format(LatencyPattern, "75\\.000%")))); BenchmarksEventSource.Measure("wrk2/latency/90", ReadLatency(Regex.Match(output, String.Format(LatencyPattern, "90\\.000%")))); BenchmarksEventSource.Measure("wrk2/latency/99", ReadLatency(Regex.Match(output, String.Format(LatencyPattern, "99\\.000%")))); BenchmarksEventSource.Measure("wrk2/latency/99.9", ReadLatency(Regex.Match(output, String.Format(LatencyPattern, "99\\.900%")))); BenchmarksEventSource.Measure("wrk2/latency/99.99", ReadLatency(Regex.Match(output, String.Format(LatencyPattern, "99\\.990%")))); BenchmarksEventSource.Measure("wrk2/latency/99.999", ReadLatency(Regex.Match(output, String.Format(LatencyPattern, "99\\.999%")))); BenchmarksEventSource.Measure("wrk2/latency/100", ReadLatency(Regex.Match(output, String.Format(LatencyPattern, "100\\.000%")))); using (var sr = new StringReader(output)) { var line = ""; do { line = sr.ReadLine(); } while (line != null && !line.Contains("Detailed Percentile spectrum:")); var doc = new JArray(); if (line != null) { sr.ReadLine(); sr.ReadLine(); line = sr.ReadLine(); while (line != null && !line.StartsWith("#")) { Console.WriteLine("Analyzing: " + line); var values = line.Split(' ', StringSplitOptions.RemoveEmptyEntries); doc.Add( new JObject( new JProperty("latency_us", decimal.Parse(values[0], CultureInfo.InvariantCulture)), new JProperty("count", decimal.Parse(values[2], CultureInfo.InvariantCulture)), new JProperty("percentile", decimal.Parse(values[1], CultureInfo.InvariantCulture)) )); line = sr.ReadLine(); } } BenchmarksEventSource.Measure("wrk2/latency/distribution", doc.ToString()); } } return(0); }
static int RunCore(string fileName, string[] args, bool parseLatency) { // Extracting duration parameters string warmup = ""; string duration = ""; var argsList = args.ToList(); var durationIndex = argsList.FindIndex(x => String.Equals(x, "-d", StringComparison.OrdinalIgnoreCase)); if (durationIndex >= 0) { duration = argsList[durationIndex + 1]; argsList.RemoveAt(durationIndex); argsList.RemoveAt(durationIndex); } else { Console.WriteLine("Couldn't find -d argument"); return(-1); } var warmupIndex = argsList.FindIndex(x => String.Equals(x, "-w", StringComparison.OrdinalIgnoreCase)); if (warmupIndex >= 0) { warmup = argsList[warmupIndex + 1]; argsList.RemoveAt(warmupIndex); argsList.RemoveAt(warmupIndex); } args = argsList.Select(Quote).ToArray(); var baseArguments = String.Join(' ', args); var process = new Process() { StartInfo = { FileName = fileName, RedirectStandardOutput = true, UseShellExecute = false, }, EnableRaisingEvents = true }; var stringBuilder = new StringBuilder(); process.OutputDataReceived += (_, e) => { if (e != null && e.Data != null) { Console.WriteLine(e.Data); lock (stringBuilder) { stringBuilder.AppendLine(e.Data); } } }; // Warmup if (!String.IsNullOrEmpty(warmup) && warmup != "0s") { process.StartInfo.Arguments = $"-d {warmup} {baseArguments}"; Console.WriteLine("> wrk " + process.StartInfo.Arguments); process.Start(); process.WaitForExit(); if (process.ExitCode != 0) { return(process.ExitCode); } } else { Console.WriteLine("Warmup skipped"); } lock (stringBuilder) { stringBuilder.Clear(); } if (!String.IsNullOrEmpty(duration) && duration != "0s") { process.StartInfo.Arguments = $"-d {duration} {baseArguments}"; Console.WriteLine("> wrk " + process.StartInfo.Arguments); process.Start(); BenchmarksEventSource.SetChildProcessId(process.Id); process.BeginOutputReadLine(); process.WaitForExit(); if (process.ExitCode != 0) { return(process.ExitCode); } string output; lock (stringBuilder) { output = stringBuilder.ToString(); } BenchmarksEventSource.Register("wrk/rps/mean;http/rps/mean", Operations.Max, Operations.Sum, "Requests/sec", "Requests per second", "n0"); BenchmarksEventSource.Register("wrk/requests;http/requests", Operations.Max, Operations.Sum, "Requests", "Total number of requests", "n0"); BenchmarksEventSource.Register("wrk/latency/mean;http/latency/mean", Operations.Max, Operations.Avg, "Mean latency (ms)", "Mean latency (ms)", "n2"); BenchmarksEventSource.Register("wrk/latency/max;http/latency/max", Operations.Max, Operations.Max, "Max latency (ms)", "Max latency (ms)", "n2"); BenchmarksEventSource.Register("wrk/errors/badresponses;http/requests/badresponses", Operations.Max, Operations.Sum, "Bad responses", "Non-2xx or 3xx responses", "n0"); BenchmarksEventSource.Register("wrk/errors/socketerrors;http/requests/errors", Operations.Max, Operations.Sum, "Socket errors", "Socket errors", "n0"); BenchmarksEventSource.Register("wrk/throughput;http/throughput", Operations.Max, Operations.Sum, "Read throughput (MB/s)", "Read throughput (MB/s)", "n2"); /* Sample output * > wrk -d 15s -c 1024 http://10.0.0.102:5000/plaintext --latency -t 32 * Running 15s test @ http://10.0.0.102:5000/plaintext * 32 threads and 1024 connections * Thread Stats Avg Stdev Max +/- Stdev * Latency 0.93ms 1.55ms 63.85ms 97.35% * Req/Sec 364.39k 34.02k 1.08M 89.87% * Latency Distribution * 50% 717.00us * 75% 1.05ms * 90% 1.47ms * 99% 6.80ms * 174818486 requests in 15.10s, 20.51GB read * Requests/sec: 11577304.21 * Transfer/sec: 1.36GB */ const string LatencyPattern = @"\s+{0}\s+([\d\.]+)(\w+)"; var latencyMatch = Regex.Match(output, String.Format(LatencyPattern, "Latency")); BenchmarksEventSource.Measure("wrk/latency/mean;http/latency/mean", ReadLatency(latencyMatch)); var rpsMatch = Regex.Match(output, @"Requests/sec:\s*([\d\.]*)"); if (rpsMatch.Success && rpsMatch.Groups.Count == 2) { BenchmarksEventSource.Measure("wrk/rps/mean;http/rps/mean", double.Parse(rpsMatch.Groups[1].Value)); } var throughputMatch = Regex.Match(output, @"Transfer/sec:\s+([\d\.]+)(\w+)"); BenchmarksEventSource.Measure("wrk/throughput;http/throughput", ReadThroughput(throughputMatch)); // Max latency is 3rd number after "Latency " var maxLatencyMatch = Regex.Match(output, @"\s+Latency\s+[\d\.]+\w+\s+[\d\.]+\w+\s+([\d\.]+)(\w+)"); BenchmarksEventSource.Measure("wrk/latency/max;http/latency/max", ReadLatency(maxLatencyMatch)); var requestsCountMatch = Regex.Match(output, @"([\d\.]*) requests in ([\d\.]*)(\w*)"); BenchmarksEventSource.Measure("wrk/requests;http/requests", ReadRequests(requestsCountMatch)); var badResponsesMatch = Regex.Match(output, @"Non-2xx or 3xx responses: ([\d\.]*)"); BenchmarksEventSource.Measure("wrk/errors/badresponses;http/requests/badresponses", ReadBadResponses(badResponsesMatch)); var socketErrorsMatch = Regex.Match(output, @"Socket errors: connect ([\d\.]*), read ([\d\.]*), write ([\d\.]*), timeout ([\d\.]*)"); BenchmarksEventSource.Measure("wrk/errors/socketerrors;http/requests/errors", CountSocketErrors(socketErrorsMatch)); if (parseLatency) { BenchmarksEventSource.Register("wrk/latency/50;http/latency/50", Operations.Max, Operations.Max, "Latency 50th (ms)", "Latency 50th (ms)", "n2"); BenchmarksEventSource.Register("wrk/latency/75;http/latency/75", Operations.Max, Operations.Max, "Latency 75th (ms)", "Latency 75th (ms)", "n2"); BenchmarksEventSource.Register("wrk/latency/90;http/latency/90", Operations.Max, Operations.Max, "Latency 90th (ms)", "Latency 90th (ms)", "n2"); BenchmarksEventSource.Register("wrk/latency/99;http/latency/99", Operations.Max, Operations.Max, "Latency 99th (ms)", "Latency 99th (ms)", "n2"); BenchmarksEventSource.Measure("wrk/latency/50;http/latency/50", ReadLatency(Regex.Match(output, string.Format(LatencyPattern, "50%")))); BenchmarksEventSource.Measure("wrk/latency/75;http/latency/75", ReadLatency(Regex.Match(output, string.Format(LatencyPattern, "75%")))); BenchmarksEventSource.Measure("wrk/latency/90;http/latency/90", ReadLatency(Regex.Match(output, string.Format(LatencyPattern, "90%")))); BenchmarksEventSource.Measure("wrk/latency/99;http/latency/99", ReadLatency(Regex.Match(output, string.Format(LatencyPattern, "99%")))); } } else { Console.WriteLine("Benchmark skipped"); } return(0); }
static async Task <int> Main(string[] args) { Console.WriteLine("Bombardier Client"); Console.WriteLine("args: " + String.Join(' ', args)); Console.Write("Measuring first request ... "); await MeasureFirstRequest(args); // Extracting parameters var argsList = args.ToList(); TryGetArgumentValue("-w", argsList, out int warmup); TryGetArgumentValue("-d", argsList, out int duration); TryGetArgumentValue("-n", argsList, out int requests); if (duration == 0 && requests == 0) { Console.WriteLine("Couldn't find valid -d and -n arguments (integers)"); return(-1); } TryGetArgumentValue("-w", argsList, out warmup); args = argsList.ToArray(); string bombardierUrl = null; if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { bombardierUrl = "https://github.com/codesenberg/bombardier/releases/download/v1.2.4/bombardier-windows-amd64.exe"; } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { bombardierUrl = "https://github.com/codesenberg/bombardier/releases/download/v1.2.4/bombardier-linux-amd64"; } else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { bombardierUrl = "https://github.com/codesenberg/bombardier/releases/download/v1.2.4/bombardier-darwin-amd64"; } else { Console.WriteLine("Unsupported platform"); return(-1); } var cacheFolder = Path.Combine(Path.GetTempPath(), ".crank"); if (!Directory.Exists(cacheFolder)) { Directory.CreateDirectory(cacheFolder); } var bombardierFileName = Path.Combine(cacheFolder, Path.GetFileName(bombardierUrl)); Console.WriteLine($"Downloading bombardier from {bombardierUrl} to {bombardierFileName}"); using (var downloadStream = await _httpClient.GetStreamAsync(bombardierUrl)) using (var fileStream = File.Create(bombardierFileName)) { await downloadStream.CopyToAsync(fileStream); } if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { Console.WriteLine($"Setting execute permission on executable {bombardierFileName}"); Process.Start("chmod", "+x " + bombardierFileName); } if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { Console.WriteLine($"Allow running bombardier"); Process.Start("spctl", "--add " + bombardierFileName); } args = argsList.Select(Quote).ToArray(); var baseArguments = String.Join(' ', args) + " --print r --format json"; var process = new Process() { StartInfo = { FileName = bombardierFileName, RedirectStandardOutput = true, UseShellExecute = false, }, EnableRaisingEvents = true }; var stringBuilder = new StringBuilder(); process.OutputDataReceived += (_, e) => { if (e != null && e.Data != null) { Console.WriteLine(e.Data); lock (stringBuilder) { stringBuilder.AppendLine(e.Data); } } }; // Warmup if (warmup > 0) { process.StartInfo.Arguments = $" -d {warmup}s {baseArguments}"; Console.WriteLine("> bombardier " + process.StartInfo.Arguments); await StartProcessWithRetriesAsync(process); process.WaitForExit(); if (process.ExitCode != 0) { return(process.ExitCode); } } lock (stringBuilder) { stringBuilder.Clear(); } process.StartInfo.Arguments = requests > 0 ? $" -n {requests} {baseArguments}" : $" -d {duration}s {baseArguments}"; Console.WriteLine("> bombardier " + process.StartInfo.Arguments); await StartProcessWithRetriesAsync(process); BenchmarksEventSource.SetChildProcessId(process.Id); process.BeginOutputReadLine(); process.WaitForExit(); if (process.ExitCode != 0) { return(process.ExitCode); } string output; lock (stringBuilder) { output = stringBuilder.ToString(); } var document = JObject.Parse(output); BenchmarksEventSource.Register("bombardier/requests", Operations.Max, Operations.Sum, "Requests", "Total number of requests", "n0"); BenchmarksEventSource.Register("bombardier/badresponses", Operations.Max, Operations.Sum, "Bad responses", "Non-2xx or 3xx responses", "n0"); BenchmarksEventSource.Register("bombardier/latency/mean", Operations.Max, Operations.Sum, "Mean latency (us)", "Mean latency (us)", "n0"); BenchmarksEventSource.Register("bombardier/latency/max", Operations.Max, Operations.Sum, "Max latency (us)", "Max latency (us)", "n0"); BenchmarksEventSource.Register("bombardier/rps/mean", Operations.Max, Operations.Sum, "Requests/sec", "Requests per second", "n0"); BenchmarksEventSource.Register("bombardier/rps/max", Operations.Max, Operations.Sum, "Requests/sec (max)", "Max requests per second", "n0"); BenchmarksEventSource.Register("bombardier/throughput", Operations.Max, Operations.Sum, "Read throughput (MB/s)", "Read throughput (MB/s)", "n2"); BenchmarksEventSource.Register("bombardier/raw", Operations.All, Operations.All, "Raw results", "Raw results", "json"); var total = document["result"]["req1xx"].Value <long>() + document["result"]["req2xx"].Value <long>() + document["result"]["req3xx"].Value <long>() + document["result"]["req3xx"].Value <long>() + document["result"]["req4xx"].Value <long>() + document["result"]["req5xx"].Value <long>() + document["result"]["others"].Value <long>(); var success = document["result"]["req2xx"].Value <long>() + document["result"]["req3xx"].Value <long>(); BenchmarksEventSource.Measure("bombardier/requests", total); BenchmarksEventSource.Measure("bombardier/badresponses", total - success); BenchmarksEventSource.Measure("bombardier/latency/mean", document["result"]["latency"]["mean"].Value <double>()); BenchmarksEventSource.Measure("bombardier/latency/max", document["result"]["latency"]["max"].Value <double>()); BenchmarksEventSource.Measure("bombardier/rps/max", document["result"]["rps"]["max"].Value <double>()); BenchmarksEventSource.Measure("bombardier/rps/mean", document["result"]["rps"]["mean"].Value <double>()); BenchmarksEventSource.Measure("bombardier/raw", output); var bytesPerSecond = document["result"]["bytesRead"].Value <long>() / document["result"]["timeTakenSeconds"].Value <double>(); // B/s to MB/s BenchmarksEventSource.Measure("bombardier/throughput", bytesPerSecond / 1024 / 1024); return(0); }
public static void MeasureAndRegister(string name, double value, string description, string format = "n2") { BenchmarksEventSource.Register(name, Operations.First, Operations.First, description, description, format); BenchmarksEventSource.Measure(name, value); }