Skip to content

Commit 0119ec2

Browse files
authored
Unique values (dotnet#407)
* don't allow for duplicated arguments, fixes dotnet#404 * remove duplicated values, fixes dotnet#403
1 parent be864ab commit 0119ec2

File tree

4 files changed

+135
-1
lines changed

4 files changed

+135
-1
lines changed

src/benchmarks/micro/corefx/System.Runtime/Perf.UInt16.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public class Perf_UInt16
1717
public static IEnumerable<object> Values => new object[]
1818
{
1919
ushort.MinValue,
20-
0,
20+
(ushort)12345, // same value used by other tests to compare the perf
2121
ushort.MaxValue
2222
};
2323

src/harness/BenchmarkDotNet.Extensions/RecommendedConfig.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ public static IConfig Create(DirectoryInfo artifactsPath, ImmutableHashSet<strin
2525
.With(JsonExporter.Full) // make sure we export to Json (for BenchView integration purpose)
2626
.With(StatisticColumn.Median, StatisticColumn.Min, StatisticColumn.Max)
2727
.With(TooManyTestCasesValidator.FailOnError)
28+
.With(new UniqueArgumentsValidator()) // don't allow for duplicated arguments #404
2829
.With(new MandatoryCategoryValidator(mandatoryCategories));
2930
}
3031
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using BenchmarkDotNet.Validators;
6+
using System.Collections.Generic;
7+
using System.Linq;
8+
using BenchmarkDotNet.Running;
9+
10+
namespace BenchmarkDotNet.Extensions
11+
{
12+
public class UniqueArgumentsValidator : IValidator
13+
{
14+
public bool TreatsWarningsAsErrors => true;
15+
16+
public IEnumerable<ValidationError> Validate(ValidationParameters validationParameters)
17+
=> validationParameters.Benchmarks
18+
.Where(benchmark => benchmark.HasArguments || benchmark.HasParameters)
19+
.GroupBy(benchmark => (benchmark.Descriptor.Type, benchmark.Descriptor.WorkloadMethod, benchmark.Job))
20+
.Where(sameBenchmark =>
21+
{
22+
int numberOfUniqueTestCases = sameBenchmark.Distinct(new BenchmarkArgumentsComparer()).Count();
23+
int numberOfTestCases = sameBenchmark.Count();
24+
25+
return numberOfTestCases != numberOfUniqueTestCases;
26+
})
27+
.Select(duplicate => new ValidationError(true, $"Benchmark Arguments should be unique, {duplicate.Key.Type}.{duplicate.Key.WorkloadMethod} has duplicate arguments.", duplicate.First()));
28+
29+
private class BenchmarkArgumentsComparer : IEqualityComparer<BenchmarkCase>
30+
{
31+
public bool Equals(BenchmarkCase x, BenchmarkCase y)
32+
=> Enumerable.SequenceEqual(
33+
x.Parameters.Items.Select(argument => argument.Value),
34+
y.Parameters.Items.Select(argument => argument.Value));
35+
36+
public int GetHashCode(BenchmarkCase obj)
37+
=> obj.Parameters.Items
38+
.Where(item => item.Value != null)
39+
.Aggregate(seed: 0, (hashCode, argument) => hashCode ^= argument.Value.GetHashCode());
40+
}
41+
}
42+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Collections.Generic;
7+
using System.Linq;
8+
using System.Reflection;
9+
using BenchmarkDotNet.Attributes;
10+
using BenchmarkDotNet.Extensions;
11+
using BenchmarkDotNet.Running;
12+
using BenchmarkDotNet.Validators;
13+
using Xunit;
14+
15+
namespace Tests
16+
{
17+
public class UniqueArgumentsValidatorTests
18+
{
19+
[Theory]
20+
[InlineData(typeof(WithDuplicateValueTypeArguments), true)]
21+
[InlineData(typeof(WithDuplicateReferenceTypeArguments), true)]
22+
[InlineData(typeof(WithoutDuplicatedValueTypeArguments), false)]
23+
[InlineData(typeof(WithoutDuplicatedReferenceTypeArguments), false)]
24+
public void DuplicatedArgumentsAreDetected(Type typeWithBenchmarks, bool shouldReportError)
25+
{
26+
var benchmarksForType = BenchmarkConverter.TypeToBenchmarks(typeWithBenchmarks);
27+
var validationParameters = new ValidationParameters(benchmarksForType.BenchmarksCases, benchmarksForType.Config);
28+
29+
var validationErrors = new UniqueArgumentsValidator().Validate(validationParameters);
30+
31+
if (shouldReportError)
32+
Assert.NotEmpty(validationErrors);
33+
else
34+
Assert.Empty(validationErrors);
35+
}
36+
37+
public class WithDuplicateValueTypeArguments
38+
{
39+
public static IEnumerable<object> Values => new object[]
40+
{
41+
ushort.MinValue, // 0
42+
(ushort)0, // ushort.MinValue
43+
ushort.MaxValue
44+
};
45+
46+
[Benchmark]
47+
[ArgumentsSource(nameof(Values))]
48+
public string ToString(ushort value) => value.ToString();
49+
}
50+
51+
public class WithDuplicateReferenceTypeArguments
52+
{
53+
public static IEnumerable<object> Values => new object[]
54+
{
55+
"",
56+
string.Empty,
57+
"something"
58+
};
59+
60+
[Benchmark]
61+
[ArgumentsSource(nameof(Values))]
62+
public int GetHashCode(string value) => value.GetHashCode();
63+
}
64+
65+
public class WithoutDuplicatedValueTypeArguments
66+
{
67+
public static IEnumerable<object> Values => new object[]
68+
{
69+
ushort.MinValue,
70+
ushort.MaxValue
71+
};
72+
73+
[Benchmark]
74+
[ArgumentsSource(nameof(Values))]
75+
public string ToString(ushort value) => value.ToString();
76+
}
77+
78+
public class WithoutDuplicatedReferenceTypeArguments
79+
{
80+
public static IEnumerable<object> Values => new object[]
81+
{
82+
"",
83+
"something"
84+
};
85+
86+
[Benchmark]
87+
[ArgumentsSource(nameof(Values))]
88+
public int GetHashCode(string value) => value.GetHashCode();
89+
}
90+
}
91+
}

0 commit comments

Comments
 (0)