private static async Task <RedisClient> CreateClient(int clientNumber, BenchmarkConfiguration config, CancellationToken ct) { var options = new ConfigurationOptions { Password = config.Password, ClientName = "redis-benchmark-client", DefaultDatabase = config.DatabaseNumber, }; options.EndPoints.Add(config.Endpoint); var connection = await ConnectionMultiplexer.ConnectAsync(options); var faker = new Faker(); var seedDateTimeOffset = faker.Date.BetweenOffset(DateTimeOffset.MinValue, DateTimeOffset.MaxValue); var rnd = new Random((int)seedDateTimeOffset.ToUnixTimeSeconds()); var bytes = new byte[(int)config.ValueSize.Bytes]; Log.Verbose("Client #{ClientNumber} created.", clientNumber); return(new RedisClient { ClientNumber = clientNumber, Config = config, Connection = connection, Db = connection.GetDatabase(config.DatabaseNumber), Faker = faker, Random = rnd, Bytes = bytes, }); }
private static async Task ExecuteBenchmarks(BenchmarkConfiguration config) { if (config.IsBenchmarkEnabled("ping")) { await ExecuteTest("PING", async client => { await client.Db.PingAsync(); }, config, cts.Token); } if (config.IsBenchmarkEnabled("set")) { await ExecuteTest("SET", async client => { client.Random.NextBytes(client.Bytes); await client.Db.StringSetAsync(client.GetKey("foo"), client.Bytes); }, config, cts.Token); } if (config.IsBenchmarkEnabled("get")) { await ExecuteTest("GET", async client => { await client.Db.StringGetAsync(client.GetKey("foo")); }, config, cts.Token); } if (config.IsBenchmarkEnabled("del")) { await ExecuteTest("DEL", async client => { await client.Db.KeyDeleteAsync(client.GetKey("foo")); }, config, cts.Token); } }
private static async Task <IEnumerable <RedisClient> > CreateClients(BenchmarkConfiguration config, CancellationToken ct) { if (config.LongLivedConnections && StaticClients != null) { return(StaticClients); } var clientTasks = Enumerable.Range(0, config.ClientCount).Select(num => CreateClient(num, config, ct)); var clients = await Task.WhenAll(clientTasks); if (config.LongLivedConnections) { StaticClients = clients; } return(clients); }
static void Main(string[] args) { Console.CancelKeyPress += (sender, cancelArgs) => { Log.Verbose("Cancelling execution..."); cts.Cancel(); }; var app = new CommandLineApplication { Name = "redis-benchmark", FullName = "Minimal redis-benchmark clone using StackExchange.Redis", ExtendedHelpText = $@" The version of StackExchange.Redis can be changed by specifying the environment variable StackExchangeRedisVersion and providing a value that is a valid version of the NuGet package. If no value is specified it will default to {HighestTestedVersion}. Note that the version must be compatible with the APIs used in the tool. The lowest version tested was {LowestTestedVersion} and the highest was {HighestTestedVersion}." }; app.HelpOption("-?|--help"); var hostOption = app.Option("-h|--host", "Server hostname (default 127.0.0.1)", CommandOptionType.SingleValue, false); var portOption = app.Option("-p|--port", "Server port (default 6379)", CommandOptionType.SingleValue, false); var socketOption = app.Option("-s|--socket", "Server socket (overrides host and port)", CommandOptionType.SingleValue, false); var passwordOption = app.Option("-a|--auth-password", "Password for Redis Auth", CommandOptionType.SingleValue, false); var clientsOption = app.Option("-c|--clients", "Number of parallel connections (default 50)", CommandOptionType.SingleValue, false); var requestsOption = app.Option("-n|--num-requests", "Total number of requests (default 100000)", CommandOptionType.SingleValue, false); var sizeOption = app.Option("-d|--data-size", "Data size of SET/GET value, specified (default 2B)", CommandOptionType.SingleValue, false); var dbNumOption = app.Option("--dbnum", "SELECT the specified db number (default 0)", CommandOptionType.SingleValue, false); var keyspaceLenOption = app.Option("-r|--keyspace-length", @"Use random keys for SET/GET/INCR, random values for SADD Using this option the benchmark will expand the string __rand_int__ inside an argument with a 12 digits number in the specified range from 0 to keyspacelen - 1.The substitution changes every time a command is executed.Default tests use this to hit random keys in the specified range.", CommandOptionType.SingleValue, false); var quietOption = app.Option("-q|--quiet", "Quiet. Just show query/sec values", CommandOptionType.NoValue, false); var loopOption = app.Option("-l|--loop", "Loop. Run the tests forever", CommandOptionType.NoValue, false); var testsOption = app.Option("-t|--tests", "Only run the comma separated list of tests. The test names are the same as the ones produced as output", CommandOptionType.SingleValue, false); var longLivedConnectionsOption = app.Option("-L|--long-lived-connections", "Use long lived connections for all test runs", CommandOptionType.NoValue, false); app.OnExecute(async() => { var config = new BenchmarkConfiguration(); if (socketOption.HasValue()) { var values = socketOption.Value().Split(":"); config.Endpoint = new IPEndPoint(IPAddress.Parse(values[0]), int.Parse(values[1])); } else if (hostOption.HasValue() || portOption.HasValue()) { var host = IPAddress.Parse(hostOption.HasValue() ? hostOption.Value() : "127.0.0.1"); var port = int.Parse(portOption.HasValue() ? portOption.Value() : "6379"); config.Endpoint = new IPEndPoint(host, port); } if (testsOption.HasValue()) { var tests = testsOption.Value().ToLower().Split(","); config.BenchmarkTests = new HashSet <string>(tests); } if (passwordOption.HasValue()) { config.Password = passwordOption.Value(); } if (clientsOption.HasValue()) { config.ClientCount = int.Parse(clientsOption.Value()); } if (requestsOption.HasValue()) { config.RequestCount = int.Parse(requestsOption.Value()); } if (sizeOption.HasValue()) { config.ValueSize = ByteSize.Parse(sizeOption.Value()); } if (dbNumOption.HasValue()) { config.DatabaseNumber = int.Parse(dbNumOption.Value()); } if (keyspaceLenOption.HasValue()) { config.KeyspaceLength = int.Parse(keyspaceLenOption.Value()); } if (quietOption.HasValue()) { config.QuietOutput = true; } if (loopOption.HasValue()) { config.LoopIndefinitely = true; } if (longLivedConnectionsOption.HasValue()) { config.LongLivedConnections = true; } Log.Logger = new LoggerConfiguration() .MinimumLevel.Is(config.QuietOutput ? LogEventLevel.Information : LogEventLevel.Verbose) .WriteTo.Console() .CreateLogger(); if (!config.LoopIndefinitely) { await ExecuteBenchmarks(config); } else { while (true) { await ExecuteBenchmarks(config); } } // Clean up all connections if long lived connections are configured if (config.LongLivedConnections && StaticClients != null) { foreach (var client in StaticClients) { client.Dispose(); } StaticClients = null; } return(0); }); app.Execute(args); }
private static async Task ExecuteTest( string operation, Func <RedisClient, Task> testFunc, BenchmarkConfiguration config, CancellationToken ct) { if (ct.IsCancellationRequested) { return; } var clients = await CreateClients(config, ct); var testTasks = clients.Select(client => { return(Task.Factory.StartNew( async() => { for (var i = 0; i < config.RequestCount; ++i) { if (i % 100 == 0) { if (Log.IsEnabled(LogEventLevel.Debug)) { Log.Verbose("Client #{ClientNumber} has sent {Number} of {Total} {Operation}.", client.ClientNumber, i, config.RequestCount, operation); } if (ct.IsCancellationRequested) { break; } } try { await testFunc(client); } catch (Exception e) { Log.Error(e, "Error occurred when running test for Client #{ClientNumber} for {Operation}.", client.ClientNumber, operation); } } }, ct, TaskCreationOptions.LongRunning, TaskScheduler.Default ).Unwrap()); }); var stopwatch = new Stopwatch(); stopwatch.Start(); await Task.WhenAll(testTasks); stopwatch.Stop(); var totalRequests = config.ClientCount * config.RequestCount; var requestsPerSecond = totalRequests / stopwatch.Elapsed.TotalSeconds; Log.Information("{Operation}: {RequestPerSecond:F3} requests per second ({TotalRequests} requests over {TotalRuntime:F3} seconds)", operation, requestsPerSecond, totalRequests, stopwatch.Elapsed.TotalSeconds); // Dispose all connections if it is desired to recreate them per test, otherwise keep them live if (!config.LongLivedConnections) { foreach (var client in clients) { client.Dispose(); } clients = null; } }