Skip to content

Commit e8b5d87

Browse files
authored
Merge pull request #269 from nblumhardt/2023.1-target-cases
2023.1 target cases
2 parents 626bac2 + 93bd82a commit e8b5d87

File tree

6 files changed

+119
-83
lines changed

6 files changed

+119
-83
lines changed

src/SeqCli/Cli/Commands/Bench/BenchCase.cs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,18 @@
1313
// limitations under the License.
1414

1515
#nullable enable
16-
using System.Collections.Generic;
1716

18-
namespace SeqCli.Cli.Commands;
17+
namespace SeqCli.Cli.Commands.Bench;
18+
19+
// ReSharper disable ClassNeverInstantiated.Global AutoPropertyCanBeMadeGetOnly.Global UnusedAutoPropertyAccessor.Global
1920

2021
class BenchCase
2122
{
22-
public string Id = "";
23-
public string Query = "";
24-
public string SignalExpression = "";
23+
public string Id { get; set; } = null!;
24+
public string Query { get; set; } = null!;
25+
public string? SignalExpression { get; set; }
26+
27+
// Not used programmatically at this time.
28+
// ReSharper disable once UnusedMember.Global
29+
public string? Notes { get; set; }
2530
}

src/SeqCli/Cli/Commands/Bench/BenchCaseTimings.cs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,31 +17,33 @@
1717
using System.Collections.Generic;
1818
using System.Linq;
1919

20-
namespace SeqCli.Cli.Commands;
20+
namespace SeqCli.Cli.Commands.Bench;
2121

2222
/*
2323
* Collects benchmarking elapsed time measurements and calculates statistics.
2424
*/
2525
class BenchCaseTimings
2626
{
27-
readonly List<double> _elaspseds = new() { };
28-
public double MeanElapsed => _elaspseds.Sum() / _elaspseds.Count;
29-
public double MinElapsed => _elaspseds.Min();
30-
public double MaxElapsed => _elaspseds.Max();
31-
public double StandardDeviationElapsed => StandardDeviation(_elaspseds);
32-
public double RelativeStandardDeviationElapsed => StandardDeviation(_elaspseds) / MeanElapsed;
27+
readonly List<double> _timings = new();
28+
29+
public double MeanElapsed => _timings.Sum() / _timings.Count;
30+
public double MinElapsed => _timings.Min();
31+
public double MaxElapsed => _timings.Max();
32+
public double StandardDeviationElapsed => StandardDeviation(_timings);
33+
public double RelativeStandardDeviationElapsed => StandardDeviation(_timings) / MeanElapsed;
3334

3435
public void PushElapsed(double elapsed)
3536
{
36-
_elaspseds.Add(elapsed);
37+
_timings.Add(elapsed);
3738
}
3839

39-
double StandardDeviation(IList<double> population)
40+
static double StandardDeviation(ICollection<double> population)
4041
{
4142
if (population.Count < 2)
4243
{
4344
return 0;
4445
}
46+
4547
var mean = population.Sum() / population.Count;
4648
return Math.Sqrt(population.Select(e => Math.Pow(e - mean, 2)).Sum() / (population.Count - 1));
4749
}
Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,54 @@
11
{
22
"cases": [
33
{
4-
"id": "count-star",
5-
"query": "select count(*) from stream"
4+
"id": "count-all",
5+
"query": "select count(*) from stream where @Timestamp >= now() - 30d",
6+
"notes": "Tests page traversal performance only; avoids data copies, serialization, and evaluation."
67
},
78
{
8-
"id": "starts-with",
9-
"query": "select count(*) from stream where @Message like '%abcde'"
9+
"id": "count-having-request-id",
10+
"query": "select count(*) from stream where RequestId is not null and @Timestamp >= now() - 30d",
11+
"notes": "Tests sparse deserialization and condition evaluation atop basic page traversal."
1012
},
1113
{
12-
"id": "without-signal",
13-
"query": "select count(*) from stream where @Level = 'Warning'"
14+
"id": "count-exception-starts-with-s",
15+
"query": "select count(*) from stream where @Exception like 'S%' and @Timestamp >= now() - 30d",
16+
"notes": "Text search performance. Chooses 'S' because in .NET data there should be some hits."
1417
},
1518
{
16-
"id": "property-match",
17-
"query": "select count(*) from stream where Action = 'ListAsync'"
19+
"id": "count-message-starts-with-e",
20+
"query": "select count(*) from stream where @Message like 'E%' and @Timestamp >= now() - 30d",
21+
"notes": "Text search performance; worse on @Message than other properties because fragment pre-filtering is not used."
22+
},
23+
{
24+
"id": "count-by-level",
25+
"query": "select count(*) from stream where @Timestamp >= now() - 30d group by @Level",
26+
"notes": "Grouping performance, strings, small number of groups."
27+
},
28+
{
29+
"id": "count-by-millisecond",
30+
"query": "select count(*) from stream where @Timestamp >= now() - 30d group by @Timestamp % 1ms limit 100",
31+
"notes": "Grouping performance, numbers, up to 10000 groups."
32+
},
33+
{
34+
"id": "count-by-day",
35+
"query": "select count(*) from stream where @Timestamp >= now() - 30d group by time(12h)",
36+
"notes": "Time partitioning performance."
37+
},
38+
{
39+
"id": "distinct-exception-20ch-limit-100",
40+
"query": "select distinct(substring(@Exception, 0, 20)) from stream where @Exception is not null and @Timestamp >= now() - 30d limit 100",
41+
"notes": "Distinct on computed text."
42+
},
43+
{
44+
"id": "order-by-ts-mod-10k",
45+
"query": "select @Timestamp from stream where @Timestamp >= now() - 30d order by @Timestamp % 1ms limit 10",
46+
"notes": "Tests performance of limited sort."
47+
},
48+
{
49+
"id": "order-by-message-limit-10",
50+
"query": "select @Message from stream where @Timestamp >= now() - 30d order by @Message limit 10",
51+
"notes": "Adds character collation to limited sort."
1852
}
1953
]
20-
}
54+
}

src/SeqCli/Cli/Commands/Bench/BenchCasesCollection.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,13 @@
1515
#nullable enable
1616
using System.Collections.Generic;
1717

18-
namespace SeqCli.Cli.Commands;
18+
namespace SeqCli.Cli.Commands.Bench;
1919

2020
/*
2121
* A target type for deserialization of bench case files.
2222
*/
2323
class BenchCasesCollection
2424
{
25-
// An identifier for the particular cases file
26-
public string CasesHash = "";
27-
public IList<BenchCase> Cases = new List<BenchCase>();
28-
}
25+
// ReSharper disable once CollectionNeverUpdated.Global
26+
public IList<BenchCase> Cases { get; } = new List<BenchCase>();
27+
}

src/SeqCli/Cli/Commands/Bench/BenchCommand.cs

Lines changed: 50 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ namespace SeqCli.Cli.Commands.Bench;
6565
class BenchCommand : Command
6666
{
6767
readonly SeqConnectionFactory _connectionFactory;
68-
int _runs = 3;
68+
int _runs = 10;
6969
readonly ConnectionFeature _connection;
7070
readonly DateRangeFeature _range;
7171
string _cases = "";
@@ -76,10 +76,7 @@ class BenchCommand : Command
7676
public BenchCommand(SeqConnectionFactory connectionFactory)
7777
{
7878
_connectionFactory = connectionFactory;
79-
Options.Add("r|runs=", "The number of runs to execute", r =>
80-
{
81-
int.TryParse(r, out _runs);
82-
});
79+
Options.Add("r|runs=", "The number of runs to execute; the default is 10", r => _runs = int.Parse(r));
8380

8481
Options.Add(
8582
"c|cases=",
@@ -108,56 +105,63 @@ protected override async Task<int> Run()
108105
try
109106
{
110107
var connection = _connectionFactory.Connect(_connection);
111-
using var reportingLogger = BuildReportingLogger();
108+
var seqVersion = (await connection.Client.GetRootAsync()).Version;
109+
112110
var cases = ReadCases(_cases);
113-
var runId = Guid.NewGuid().ToString("N").Substring(0, 4);
111+
var runId = Guid.NewGuid().ToString("N")[..16];
112+
113+
await using var reportingLogger = BuildReportingLogger();
114114

115-
if (_range.Start == null || _range.End == null)
115+
using (!string.IsNullOrWhiteSpace(_description)
116+
? LogContext.PushProperty("Description", _description)
117+
: null)
116118
{
117-
Log.Error("Both the `start` and `end` arguments are required");
118-
return 1;
119+
reportingLogger.Information(
120+
"Bench run {RunId} against {ServerUrl} ({SeqVersion}); {CaseCount} cases, {Runs} runs, from {Start} to {End}",
121+
runId, connection.Client.ServerUrl, seqVersion, cases.Cases.Count, _runs, _range.Start, _range.End);
119122
}
120123

121-
foreach (var c in cases.Cases)
124+
using (LogContext.PushProperty("RunId", runId))
125+
using (LogContext.PushProperty("Start", _range.Start))
126+
using (LogContext.PushProperty("End", _range.End))
122127
{
123-
var timings = new BenchCaseTimings();
124-
object? lastResult = null;
125-
126-
foreach (var i in Enumerable.Range(1, _runs))
128+
foreach (var c in cases.Cases)
127129
{
128-
var response = await connection.Data.QueryAsync(
129-
c.Query,
130-
_range.Start,
131-
_range.End,
132-
SignalExpressionPart.Signal(c.SignalExpression)
133-
);
130+
var timings = new BenchCaseTimings();
131+
object? lastResult = null;
134132

135-
timings.PushElapsed(response.Statistics.ElapsedMilliseconds);
136-
137-
if (response.Rows != null)
133+
foreach (var i in Enumerable.Range(1, _runs))
138134
{
139-
var isScalarResult = response.Rows.Length == 1 && response.Rows[0].Length == 1;
140-
if (isScalarResult && i == _runs)
135+
var response = await connection.Data.QueryAsync(
136+
c.Query,
137+
_range.Start,
138+
_range.End,
139+
c.SignalExpression != null ? SignalExpressionPart.Signal(c.SignalExpression) : null
140+
);
141+
142+
timings.PushElapsed(response.Statistics.ElapsedMilliseconds);
143+
144+
if (response.Rows != null)
141145
{
142-
lastResult = response.Rows[0][0];
146+
var isScalarResult = response.Rows.Length == 1 && response.Rows[0].Length == 1;
147+
if (isScalarResult && i == _runs)
148+
{
149+
lastResult = response.Rows[0][0];
150+
}
143151
}
144152
}
145-
}
146-
147-
using (lastResult != null ? LogContext.PushProperty("LastResult", lastResult) : null)
148-
using (LogContext.PushProperty("MinElapsed", timings.MinElapsed))
149-
using (LogContext.PushProperty("MaxElapsed", timings.MaxElapsed))
150-
using (LogContext.PushProperty("Runs", _runs))
151-
using (!string.IsNullOrWhiteSpace(c.SignalExpression) ? LogContext.PushProperty("SignalExpression", c.SignalExpression) : null)
152-
using (LogContext.PushProperty("Start", _range.Start))
153-
using (LogContext.PushProperty("StandardDeviationElapsed", timings.StandardDeviationElapsed))
154-
using (LogContext.PushProperty("End", _range.End))
155-
using (LogContext.PushProperty("Query", c.Query))
156-
using (!string.IsNullOrWhiteSpace(_description) ? LogContext.PushProperty("Description", _description) : null)
157-
{
158-
reportingLogger.Information(
159-
"Bench run {Cases}/{RunId} against {Server} for query {Id}: mean {MeanElapsed:N0} ms with relative dispersion {RelativeStandardDeviationElapsed:N2}",
160-
cases.CasesHash, runId, _connection.Url, c.Id, timings.MeanElapsed, timings.RelativeStandardDeviationElapsed);
153+
154+
using (lastResult != null ? LogContext.PushProperty("LastResult", lastResult) : null)
155+
using (!string.IsNullOrWhiteSpace(c.SignalExpression)
156+
? LogContext.PushProperty("SignalExpression", c.SignalExpression)
157+
: null)
158+
using (LogContext.PushProperty("StandardDeviationElapsed", timings.StandardDeviationElapsed))
159+
using (LogContext.PushProperty("Query", c.Query))
160+
{
161+
reportingLogger.Information(
162+
"Case {Id,-40} mean {MeanElapsed,5:N0} ms (min {MinElapsed,5:N0} ms, max {MaxElapsed,5:N0} ms, RSD {RelativeStandardDeviationElapsed,4:N2})",
163+
c.Id, timings.MeanElapsed, timings.MinElapsed, timings.MaxElapsed, timings.RelativeStandardDeviationElapsed);
164+
}
161165
}
162166
}
163167

@@ -197,28 +201,20 @@ static BenchCasesCollection ReadCases(string filename)
197201
var casesString = File.ReadAllText(string.IsNullOrWhiteSpace(filename)
198202
? defaultCasesPath
199203
: filename);
204+
200205
var casesFile = JsonConvert.DeserializeObject<BenchCasesCollection>(casesString)
201206
?? new BenchCasesCollection();
202207

203-
casesFile.CasesHash = HashString(casesString);
204-
205208
if (casesFile.Cases.Select(c => c.Id).Distinct().Count() != casesFile.Cases.Count)
206209
{
207-
throw new Exception($"Cases file {filename} contains a duplicate id");
210+
throw new ArgumentException($"Cases file `{filename}` contains a duplicate id.");
208211
}
209212

210213
if (!casesFile.Cases.Any())
211214
{
212-
throw new Exception($"Cases file {filename} contains no cases");
215+
throw new ArgumentException($"Cases file `{filename}` contains no cases.");
213216
}
214217

215218
return casesFile;
216219
}
217-
218-
static string HashString(string input)
219-
{
220-
using var md5 = MD5.Create();
221-
var bytes = Encoding.ASCII.GetBytes(input);
222-
return Convert.ToHexString(md5.ComputeHash(bytes));
223-
}
224220
}

test/SeqCli.EndToEnd/Bench/BenchTestCase.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public Task ExecuteAsync(
1313
ILogger logger,
1414
CliCommandRunner runner)
1515
{
16-
var exit = runner.Exec("bench", "--start=2022-01-01 --end=2022-01-02");
16+
var exit = runner.Exec("bench", "--runs 3");
1717
Assert.Equal(0, exit);
1818

1919
return Task.CompletedTask;

0 commit comments

Comments
 (0)