Skip to content

Commit f188f76

Browse files
Respect LogicalGroup order in DefaultOrderer (see #1864) (#1866)
1 parent bb180a9 commit f188f76

12 files changed

+193
-120
lines changed

samples/BenchmarkDotNet.Samples/IntroOrderManual.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ private class Config : ManualConfig
2121

2222
private class FastestToSlowestOrderer : IOrderer
2323
{
24-
public IEnumerable<BenchmarkCase> GetExecutionOrder(ImmutableArray<BenchmarkCase> benchmarksCase) =>
24+
public IEnumerable<BenchmarkCase> GetExecutionOrder(ImmutableArray<BenchmarkCase> benchmarksCase,
25+
IEnumerable<BenchmarkLogicalGroupRule> order = null) =>
2526
from benchmark in benchmarksCase
2627
orderby benchmark.Parameters["X"] descending,
2728
benchmark.Descriptor.WorkloadMethodDisplayInfo
@@ -37,7 +38,8 @@ orderby summary[benchmark].ResultStatistics.Mean
3738
public string GetLogicalGroupKey(ImmutableArray<BenchmarkCase> allBenchmarksCases, BenchmarkCase benchmarkCase) =>
3839
benchmarkCase.Job.DisplayInfo + "_" + benchmarkCase.Parameters.DisplayInfo;
3940

40-
public IEnumerable<IGrouping<string, BenchmarkCase>> GetLogicalGroupOrder(IEnumerable<IGrouping<string, BenchmarkCase>> logicalGroups) =>
41+
public IEnumerable<IGrouping<string, BenchmarkCase>> GetLogicalGroupOrder(IEnumerable<IGrouping<string, BenchmarkCase>> logicalGroups,
42+
IEnumerable<BenchmarkLogicalGroupRule> order = null) =>
4143
logicalGroups.OrderBy(it => it.Key);
4244

4345
public bool SeparateLogicalGroups => true;

src/BenchmarkDotNet/Configs/ImmutableConfig.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public sealed class ImmutableConfig : IConfig
3030
private readonly ImmutableHashSet<Job> jobs;
3131
private readonly ImmutableHashSet<HardwareCounter> hardwareCounters;
3232
private readonly ImmutableHashSet<IFilter> filters;
33-
private readonly ImmutableHashSet<BenchmarkLogicalGroupRule> rules;
33+
private readonly ImmutableArray<BenchmarkLogicalGroupRule> rules;
3434

3535
internal ImmutableConfig(
3636
ImmutableArray<IColumnProvider> uniqueColumnProviders,
@@ -41,7 +41,7 @@ internal ImmutableConfig(
4141
ImmutableHashSet<IAnalyser> uniqueAnalyzers,
4242
ImmutableHashSet<IValidator> uniqueValidators,
4343
ImmutableHashSet<IFilter> uniqueFilters,
44-
ImmutableHashSet<BenchmarkLogicalGroupRule> uniqueRules,
44+
ImmutableArray<BenchmarkLogicalGroupRule> uniqueRules,
4545
ImmutableHashSet<Job> uniqueRunnableJobs,
4646
ConfigUnionRule unionRule,
4747
string artifactsPath,

src/BenchmarkDotNet/Configs/ImmutableConfigBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public static ImmutableConfig Create(IConfig source)
4545
var uniqueValidators = GetValidators(source.GetValidators(), MandatoryValidators, source.Options);
4646

4747
var uniqueFilters = source.GetFilters().ToImmutableHashSet();
48-
var uniqueRules = source.GetLogicalGroupRules().ToImmutableHashSet();
48+
var uniqueRules = source.GetLogicalGroupRules().ToImmutableArray();
4949

5050
var uniqueRunnableJobs = GetRunnableJobs(source.GetJobs()).ToImmutableHashSet();
5151

src/BenchmarkDotNet/Configs/ManualConfig.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public class ManualConfig : IConfig
2929
private readonly List<Job> jobs = new List<Job>();
3030
private readonly HashSet<HardwareCounter> hardwareCounters = new HashSet<HardwareCounter>();
3131
private readonly List<IFilter> filters = new List<IFilter>();
32-
private readonly HashSet<BenchmarkLogicalGroupRule> logicalGroupRules = new HashSet<BenchmarkLogicalGroupRule>();
32+
private readonly List<BenchmarkLogicalGroupRule> logicalGroupRules = new List<BenchmarkLogicalGroupRule>();
3333

3434
public IEnumerable<IColumnProvider> GetColumnProviders() => columnProviders;
3535
public IEnumerable<IExporter> GetExporters() => exporters;
@@ -191,7 +191,12 @@ public ManualConfig AddFilter(params IFilter[] newFilters)
191191

192192
public ManualConfig AddLogicalGroupRules(params BenchmarkLogicalGroupRule[] rules)
193193
{
194-
logicalGroupRules.AddRange(rules);
194+
foreach (var rule in rules)
195+
{
196+
if (logicalGroupRules.Contains(rule))
197+
logicalGroupRules.Remove(rule);
198+
logicalGroupRules.Add(rule);
199+
}
195200
return this;
196201
}
197202

src/BenchmarkDotNet/Order/DefaultOrderer.cs

Lines changed: 77 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System;
12
using System.Collections.Generic;
23
using System.Collections.Immutable;
34
using System.Diagnostics.CodeAnalysis;
@@ -19,8 +20,7 @@ public class DefaultOrderer : IOrderer
1920
private readonly IComparer<string[]> categoryComparer = CategoryComparer.Instance;
2021
private readonly IComparer<ParameterInstances> paramsComparer = ParameterComparer.Instance;
2122
private readonly IComparer<Job> jobComparer = JobComparer.Instance;
22-
private readonly IComparer<BenchmarkCase> benchmarkComparer;
23-
private readonly IComparer<IGrouping<string, BenchmarkCase>> logicalGroupComparer;
23+
private readonly IComparer<Descriptor> targetComparer;
2424

2525
public SummaryOrderPolicy SummaryOrderPolicy { get; }
2626
public MethodOrderPolicy MethodOrderPolicy { get; }
@@ -31,14 +31,15 @@ public DefaultOrderer(
3131
{
3232
SummaryOrderPolicy = summaryOrderPolicy;
3333
MethodOrderPolicy = methodOrderPolicy;
34-
IComparer<Descriptor> targetComparer = new DescriptorComparer(methodOrderPolicy);
35-
benchmarkComparer = new BenchmarkComparer(categoryComparer, paramsComparer, jobComparer, targetComparer);
36-
logicalGroupComparer = new LogicalGroupComparer(benchmarkComparer);
34+
targetComparer = new DescriptorComparer(methodOrderPolicy);
3735
}
3836

3937
[PublicAPI]
40-
public virtual IEnumerable<BenchmarkCase> GetExecutionOrder(ImmutableArray<BenchmarkCase> benchmarkCases)
38+
public virtual IEnumerable<BenchmarkCase> GetExecutionOrder(
39+
ImmutableArray<BenchmarkCase> benchmarkCases,
40+
IEnumerable<BenchmarkLogicalGroupRule> order = null)
4141
{
42+
var benchmarkComparer = new BenchmarkComparer(categoryComparer, paramsComparer, jobComparer, targetComparer, order);
4243
var list = benchmarkCases.ToList();
4344
list.Sort(benchmarkComparer);
4445
return list;
@@ -47,7 +48,7 @@ public virtual IEnumerable<BenchmarkCase> GetExecutionOrder(ImmutableArray<Bench
4748
public virtual IEnumerable<BenchmarkCase> GetSummaryOrder(ImmutableArray<BenchmarkCase> benchmarksCases, Summary summary)
4849
{
4950
var benchmarkLogicalGroups = benchmarksCases.GroupBy(b => GetLogicalGroupKey(benchmarksCases, b));
50-
foreach (var logicalGroup in GetLogicalGroupOrder(benchmarkLogicalGroups))
51+
foreach (var logicalGroup in GetLogicalGroupOrder(benchmarkLogicalGroups, benchmarksCases.FirstOrDefault()?.Config.GetLogicalGroupRules()))
5152
foreach (var benchmark in GetSummaryOrderForGroup(logicalGroup.ToImmutableArray(), summary))
5253
yield return benchmark;
5354
}
@@ -65,7 +66,7 @@ protected virtual IEnumerable<BenchmarkCase> GetSummaryOrderForGroup(ImmutableAr
6566
case SummaryOrderPolicy.Declared:
6667
return benchmarksCase;
6768
default:
68-
return GetExecutionOrder(benchmarksCase);
69+
return GetExecutionOrder(benchmarksCase, benchmarksCase.FirstOrDefault()?.Config.GetLogicalGroupRules());
6970
}
7071
}
7172

@@ -84,41 +85,62 @@ public string GetHighlightGroupKey(BenchmarkCase benchmarkCase)
8485

8586
public string GetLogicalGroupKey(ImmutableArray<BenchmarkCase> allBenchmarksCases, BenchmarkCase benchmarkCase)
8687
{
87-
var rules = new HashSet<BenchmarkLogicalGroupRule>(benchmarkCase.Config.GetLogicalGroupRules());
88+
var explicitRules = benchmarkCase.Config.GetLogicalGroupRules().ToList();
89+
var implicitRules = new List<BenchmarkLogicalGroupRule>();
8890
bool hasJobBaselines = allBenchmarksCases.Any(b => b.Job.Meta.Baseline);
8991
bool hasDescriptorBaselines = allBenchmarksCases.Any(b => b.Descriptor.Baseline);
9092
if (hasJobBaselines)
9193
{
92-
rules.Add(BenchmarkLogicalGroupRule.ByMethod);
93-
rules.Add(BenchmarkLogicalGroupRule.ByParams);
94+
implicitRules.Add(BenchmarkLogicalGroupRule.ByParams);
95+
implicitRules.Add(BenchmarkLogicalGroupRule.ByMethod);
9496
}
9597
if (hasDescriptorBaselines)
9698
{
97-
rules.Add(BenchmarkLogicalGroupRule.ByJob);
98-
rules.Add(BenchmarkLogicalGroupRule.ByParams);
99+
implicitRules.Add(BenchmarkLogicalGroupRule.ByParams);
100+
implicitRules.Add(BenchmarkLogicalGroupRule.ByJob);
99101
}
100102
if (hasJobBaselines && hasDescriptorBaselines)
101103
{
102-
rules.Remove(BenchmarkLogicalGroupRule.ByMethod);
103-
rules.Remove(BenchmarkLogicalGroupRule.ByJob);
104+
implicitRules.Remove(BenchmarkLogicalGroupRule.ByMethod);
105+
implicitRules.Remove(BenchmarkLogicalGroupRule.ByJob);
104106
}
105107

108+
var rules = new List<BenchmarkLogicalGroupRule>(explicitRules);
109+
foreach (var rule in implicitRules.Where(rule => !rules.Contains(rule)))
110+
rules.Add(rule);
111+
106112
var keys = new List<string>();
107-
if (rules.Contains(BenchmarkLogicalGroupRule.ByCategory))
108-
keys.Add(string.Join(",", benchmarkCase.Descriptor.Categories));
109-
if (rules.Contains(BenchmarkLogicalGroupRule.ByMethod))
110-
keys.Add(benchmarkCase.Descriptor.DisplayInfo);
111-
if (rules.Contains(BenchmarkLogicalGroupRule.ByJob))
112-
keys.Add(benchmarkCase.Job.DisplayInfo);
113-
if (rules.Contains(BenchmarkLogicalGroupRule.ByParams))
114-
keys.Add(benchmarkCase.Parameters.ValueInfo);
113+
foreach (var rule in rules)
114+
{
115+
switch (rule)
116+
{
117+
case BenchmarkLogicalGroupRule.ByMethod:
118+
keys.Add(benchmarkCase.Descriptor.DisplayInfo);
119+
break;
120+
case BenchmarkLogicalGroupRule.ByJob:
121+
keys.Add(benchmarkCase.Job.DisplayInfo);
122+
break;
123+
case BenchmarkLogicalGroupRule.ByParams:
124+
keys.Add(benchmarkCase.Parameters.ValueInfo);
125+
break;
126+
case BenchmarkLogicalGroupRule.ByCategory:
127+
keys.Add(string.Join(",", benchmarkCase.Descriptor.Categories));
128+
break;
129+
default:
130+
throw new ArgumentOutOfRangeException(nameof(rule), rule, $"Not supported {nameof(BenchmarkLogicalGroupRule)}");
131+
}
132+
}
115133

116134
string logicalGroupKey = string.Join("-", keys.Where(key => key != string.Empty));
117135
return logicalGroupKey == string.Empty ? "*" : logicalGroupKey;
118136
}
119137

120-
public virtual IEnumerable<IGrouping<string, BenchmarkCase>> GetLogicalGroupOrder(IEnumerable<IGrouping<string, BenchmarkCase>> logicalGroups)
138+
public virtual IEnumerable<IGrouping<string, BenchmarkCase>> GetLogicalGroupOrder(
139+
IEnumerable<IGrouping<string, BenchmarkCase>> logicalGroups,
140+
IEnumerable<BenchmarkLogicalGroupRule> order = null)
121141
{
142+
var benchmarkComparer = new BenchmarkComparer(categoryComparer, paramsComparer, jobComparer, targetComparer, order);
143+
var logicalGroupComparer = new LogicalGroupComparer(benchmarkComparer);
122144
var list = logicalGroups.ToList();
123145
list.Sort(logicalGroupComparer);
124146
return list;
@@ -128,36 +150,58 @@ public virtual IEnumerable<IGrouping<string, BenchmarkCase>> GetLogicalGroupOrde
128150

129151
private class BenchmarkComparer : IComparer<BenchmarkCase>
130152
{
153+
private static readonly BenchmarkLogicalGroupRule[] DefaultOrder =
154+
{
155+
BenchmarkLogicalGroupRule.ByCategory,
156+
BenchmarkLogicalGroupRule.ByParams,
157+
BenchmarkLogicalGroupRule.ByJob,
158+
BenchmarkLogicalGroupRule.ByMethod
159+
};
160+
131161
private readonly IComparer<string[]> categoryComparer;
132162
private readonly IComparer<ParameterInstances> paramsComparer;
133163
private readonly IComparer<Job> jobComparer;
134164
private readonly IComparer<Descriptor> targetComparer;
165+
private readonly List<BenchmarkLogicalGroupRule> order;
135166

136167
public BenchmarkComparer(
137168
IComparer<string[]> categoryComparer,
138169
IComparer<ParameterInstances> paramsComparer,
139170
IComparer<Job> jobComparer,
140-
IComparer<Descriptor> targetComparer)
171+
IComparer<Descriptor> targetComparer,
172+
IEnumerable<BenchmarkLogicalGroupRule> order)
141173
{
142174
this.categoryComparer = categoryComparer;
143175
this.targetComparer = targetComparer;
144176
this.jobComparer = jobComparer;
145177
this.paramsComparer = paramsComparer;
178+
179+
this.order = new List<BenchmarkLogicalGroupRule>();
180+
foreach (var rule in (order ?? ImmutableArray<BenchmarkLogicalGroupRule>.Empty).Concat(DefaultOrder))
181+
if (!this.order.Contains(rule))
182+
this.order.Add(rule);
146183
}
147184

148185
public int Compare(BenchmarkCase x, BenchmarkCase y)
149186
{
150187
if (x == null && y == null) return 0;
151188
if (x != null && y == null) return 1;
152189
if (x == null) return -1;
153-
return new[]
190+
191+
foreach (var rule in order)
154192
{
155-
categoryComparer?.Compare(x.Descriptor.Categories, y.Descriptor.Categories) ?? 0,
156-
paramsComparer?.Compare(x.Parameters, y.Parameters) ?? 0,
157-
jobComparer?.Compare(x.Job, y.Job) ?? 0,
158-
targetComparer?.Compare(x.Descriptor, y.Descriptor) ?? 0,
159-
string.CompareOrdinal(x.DisplayInfo, y.DisplayInfo)
160-
}.FirstOrDefault(c => c != 0);
193+
int compare = rule switch
194+
{
195+
BenchmarkLogicalGroupRule.ByMethod => targetComparer?.Compare(x.Descriptor, y.Descriptor) ?? 0,
196+
BenchmarkLogicalGroupRule.ByJob => jobComparer?.Compare(x.Job, y.Job) ?? 0,
197+
BenchmarkLogicalGroupRule.ByParams => paramsComparer?.Compare(x.Parameters, y.Parameters) ?? 0,
198+
BenchmarkLogicalGroupRule.ByCategory => categoryComparer?.Compare(x.Descriptor.Categories, y.Descriptor.Categories) ?? 0,
199+
_ => throw new ArgumentOutOfRangeException()
200+
};
201+
if (compare != 0)
202+
return compare;
203+
}
204+
return string.CompareOrdinal(x.DisplayInfo, y.DisplayInfo);
161205
}
162206
}
163207

@@ -176,4 +220,4 @@ public int Compare(IGrouping<string, BenchmarkCase> x, IGrouping<string, Benchma
176220
}
177221
}
178222
}
179-
}
223+
}

src/BenchmarkDotNet/Order/IOrderer.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System.Collections.Generic;
22
using System.Collections.Immutable;
33
using System.Linq;
4+
using BenchmarkDotNet.Configs;
45
using BenchmarkDotNet.Reports;
56
using BenchmarkDotNet.Running;
67
using JetBrains.Annotations;
@@ -10,7 +11,7 @@ namespace BenchmarkDotNet.Order
1011
public interface IOrderer
1112
{
1213
[PublicAPI, NotNull]
13-
IEnumerable<BenchmarkCase> GetExecutionOrder(ImmutableArray<BenchmarkCase> benchmarksCase);
14+
IEnumerable<BenchmarkCase> GetExecutionOrder(ImmutableArray<BenchmarkCase> benchmarksCase, IEnumerable<BenchmarkLogicalGroupRule> order = null);
1415

1516
[PublicAPI, NotNull]
1617
IEnumerable<BenchmarkCase> GetSummaryOrder(ImmutableArray<BenchmarkCase> benchmarksCases, [NotNull] Summary summary);
@@ -22,7 +23,8 @@ public interface IOrderer
2223
string GetLogicalGroupKey(ImmutableArray<BenchmarkCase> allBenchmarksCases, [NotNull] BenchmarkCase benchmarkCase);
2324

2425
[PublicAPI, NotNull]
25-
IEnumerable<IGrouping<string, BenchmarkCase>> GetLogicalGroupOrder(IEnumerable<IGrouping<string, BenchmarkCase>> logicalGroups);
26+
IEnumerable<IGrouping<string, BenchmarkCase>> GetLogicalGroupOrder(IEnumerable<IGrouping<string, BenchmarkCase>> logicalGroups,
27+
IEnumerable<BenchmarkLogicalGroupRule> order = null);
2628

2729
[PublicAPI]
2830
bool SeparateLogicalGroups { get; }

0 commit comments

Comments
 (0)