Skip to content

Commit 510685f

Browse files
blairconradadamsitnik
authored andcommitted
Partition benchmark run info based on added nuget packages (#932)
* Partition benchmark run info based on added nuget packages * fixup! Partition benchmark run info based on added nuget packages
1 parent 8a2eecd commit 510685f

File tree

6 files changed

+161
-14
lines changed

6 files changed

+161
-14
lines changed

src/BenchmarkDotNet/Jobs/JobExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,15 +192,15 @@ public static Job With(this Job job, IReadOnlyList<EnvironmentVariable> environm
192192
/// <param name="packageName">The Nuget package name</param>
193193
/// <param name="packageVersion">The Nuget package version</param>
194194
/// <returns></returns>
195-
public static Job WithNuget(this Job job, string packageName, string packageVersion) => job.WithCore(j => j.Infrastructure.NugetReferences = new HashSet<NugetReference>(j.Infrastructure.NugetReferences ?? Array.Empty<NugetReference>()) { new NugetReference(packageName, packageVersion) });
195+
public static Job WithNuget(this Job job, string packageName, string packageVersion) => job.WithCore(j => j.Infrastructure.NugetReferences = new NugetReferenceList(j.Infrastructure.NugetReferences ?? Array.Empty<NugetReference>()) { new NugetReference(packageName, packageVersion) });
196196

197197
/// <summary>
198198
/// Runs the job with a specific Nuget dependency which will be resolved during the Job build process
199199
/// </summary>
200200
/// <param name="job"></param>
201201
/// <param name="packageName">The Nuget package name, the latest version will be resolved</param>
202202
/// <returns></returns>
203-
public static Job WithNuget(this Job job, string packageName) => job.WithCore(j => j.Infrastructure.NugetReferences = new HashSet<NugetReference>(j.Infrastructure.NugetReferences ?? Array.Empty<NugetReference>()) { new NugetReference(packageName, string.Empty) });
203+
public static Job WithNuget(this Job job, string packageName) => job.WithNuget(packageName, string.Empty);
204204

205205
/// <summary>
206206
/// Runs the job with a specific Nuget dependencies which will be resolved during the Job build process

src/BenchmarkDotNet/Jobs/NugetReference.cs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,22 +29,20 @@ public override bool Equals(object obj)
2929
}
3030

3131
/// <summary>
32-
/// Object is equals when the package name is the same
32+
/// Object is equals when the package name and version are the same
3333
/// </summary>
3434
/// <param name="other"></param>
3535
/// <returns></returns>
36-
/// <remarks>
37-
/// There can only be one package reference of the same name regardless of version
38-
/// </remarks>
3936
public bool Equals(NugetReference other)
4037
{
4138
return other != null &&
42-
PackageName == other.PackageName;
39+
PackageName == other.PackageName &&
40+
PackageVersion == other.PackageVersion;
4341
}
4442

4543
public override int GetHashCode()
4644
{
47-
return 557888800 + EqualityComparer<string>.Default.GetHashCode(PackageName);
45+
return 557888800 + EqualityComparer<string>.Default.GetHashCode(PackageName) + EqualityComparer<string>.Default.GetHashCode(PackageVersion).GetHashCode();
4846
}
4947

5048
public override string ToString() => $"{PackageName}{(string.IsNullOrWhiteSpace(PackageVersion) ? string.Empty : $" {PackageVersion}")}";
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
using System;
2+
using System.Collections;
3+
using System.Collections.Generic;
4+
5+
namespace BenchmarkDotNet.Jobs
6+
{
7+
/// <summary>
8+
/// An ordered list of Nuget references. Does not allow duplicate references with the same PackageName.
9+
/// </summary>
10+
public class NugetReferenceList : IReadOnlyCollection<NugetReference>
11+
{
12+
private readonly List<NugetReference> references = new List<NugetReference>();
13+
14+
public NugetReferenceList()
15+
{
16+
}
17+
18+
public NugetReferenceList(IReadOnlyCollection<NugetReference> readOnlyCollection)
19+
{
20+
foreach (var nugetReference in readOnlyCollection)
21+
{
22+
Add(nugetReference);
23+
}
24+
}
25+
26+
public int Count => references.Count;
27+
28+
public IEnumerator<NugetReference> GetEnumerator() => references.GetEnumerator();
29+
30+
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
31+
32+
public void Add(NugetReference reference)
33+
{
34+
if (reference == null)
35+
throw new ArgumentNullException(nameof(reference));
36+
37+
var insertionIndex = references.BinarySearch(reference, PackageNameComparer.Default);
38+
if (0 <= insertionIndex && insertionIndex < Count)
39+
throw new ArgumentException($"Nuget package {reference.PackageName} was already added", nameof(reference));
40+
41+
references.Insert(~insertionIndex, reference);
42+
}
43+
44+
public override bool Equals(object obj)
45+
{
46+
if (ReferenceEquals(null, obj))
47+
return false;
48+
if (ReferenceEquals(this, obj))
49+
return true;
50+
if (obj.GetType() != GetType())
51+
return false;
52+
53+
var otherList = (NugetReferenceList)obj;
54+
if (Count != otherList.Count)
55+
return false;
56+
57+
for (int i = 0; i < Count; i++)
58+
{
59+
if (!references[i].Equals(otherList.references[i]))
60+
return false;
61+
}
62+
63+
return true;
64+
}
65+
66+
public override int GetHashCode()
67+
{
68+
unchecked
69+
{
70+
int hashCode = 0;
71+
foreach (var nugetReference in references)
72+
{
73+
hashCode = hashCode * 397 + nugetReference.GetHashCode();
74+
}
75+
76+
return hashCode;
77+
}
78+
}
79+
80+
private class PackageNameComparer : IComparer<NugetReference>
81+
{
82+
private PackageNameComparer() { }
83+
84+
public static PackageNameComparer Default { get; } = new PackageNameComparer();
85+
86+
public int Compare(NugetReference x, NugetReference y) => Comparer<string>.Default.Compare(x.PackageName, y.PackageName);
87+
}
88+
}
89+
}

src/BenchmarkDotNet/Running/BenchmarkPartitioner.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ public bool Equals(BenchmarkCase x, BenchmarkCase y)
4545
return false;
4646
if (AreDifferent(jobX.Infrastructure.Arguments, jobY.Infrastructure.Arguments)) // arguments can be anything (Mono runtime settings or MsBuild parameters)
4747
return false;
48+
if (AreDifferent(jobX.Infrastructure.NugetReferences, jobY.Infrastructure.NugetReferences))
49+
return false;
4850
if (!jobX.Environment.Gc.Equals(jobY.Environment.Gc)) // GC settings are per .config/.csproj
4951
return false;
5052

@@ -78,6 +80,8 @@ public int GetHashCode(BenchmarkCase obj)
7880
hashCode ^= job.Infrastructure.BuildConfiguration.GetHashCode();
7981
if (job.Infrastructure.Arguments != null && job.Infrastructure.Arguments.Any())
8082
hashCode ^= job.Infrastructure.Arguments.GetHashCode();
83+
if (job.Infrastructure.NugetReferences != null)
84+
hashCode ^= job.Infrastructure.NugetReferences.GetHashCode();
8185
if (!string.IsNullOrEmpty(obj.Descriptor.AdditionalLogic))
8286
hashCode ^= obj.Descriptor.AdditionalLogic.GetHashCode();
8387

tests/BenchmarkDotNet.Tests/JobTests.cs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -470,14 +470,27 @@ public static void WithNuget()
470470
Assert.Equal(1, j.Infrastructure.NugetReferences.Count);
471471

472472
j = j.WithNuget("AutoMapper", "7.0.1");
473-
Assert.Equal(2, j.Infrastructure.NugetReferences.Count); //appends
473+
Assert.Collection(j.Infrastructure.NugetReferences,
474+
reference => Assert.Equal(new NugetReference("AutoMapper", "7.0.1"), reference),
475+
reference => Assert.Equal(new NugetReference("Newtonsoft.Json", ""), reference));
474476

475-
j = j.WithNuget("AutoMapper");
476-
Assert.Equal(2, j.Infrastructure.NugetReferences.Count); //does not append, same package
477-
j = j.WithNuget("AutoMapper", "7.0.0-alpha-0001");
478-
Assert.Equal(2, j.Infrastructure.NugetReferences.Count); //does not append, same package
477+
Assert.Throws<ArgumentException>(() => j = j.WithNuget("AutoMapper")); //adding is an error, since it's the same package
478+
Assert.Throws<ArgumentException>(() => j = j.WithNuget("AutoMapper", "7.0.0-alpha-0001")); //adding is an error, since it's the same package
479479

480-
Assert.Equal("AutoMapper 7.0.1", j.Infrastructure.NugetReferences.ElementAt(1).ToString()); //first package reference in wins
480+
j = j.WithNuget("NLog", "4.5.10"); // ensure we can add at the end of a non-empty list
481+
Assert.Collection(j.Infrastructure.NugetReferences,
482+
reference => Assert.Equal(new NugetReference("AutoMapper", "7.0.1"), reference),
483+
reference => Assert.Equal(new NugetReference("Newtonsoft.Json", ""), reference),
484+
reference => Assert.Equal(new NugetReference("NLog", "4.5.10"), reference));
485+
486+
var expected = new NugetReferenceList(Array.Empty<NugetReference>())
487+
{
488+
new NugetReference("AutoMapper", "7.0.1"),
489+
new NugetReference("Newtonsoft.Json", ""),
490+
new NugetReference("NLog", "4.5.10"),
491+
};
492+
493+
Assert.Equal(expected, j.Infrastructure.NugetReferences); // ensure that the list's equality operator returns true when the contents are the same
481494
}
482495

483496
private static bool IsSubclassOfobModeOfItself(Type type)

tests/BenchmarkDotNet.Tests/Running/JobRuntimePropertiesComparerTests.cs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,5 +82,48 @@ public void CustomClrBuildJobsAreGroupedByVersion()
8282
foreach (var grouping in grouped)
8383
Assert.Equal(3 * 2, grouping.Count()); // (M1 + M2 + M3) * (Plain1 + Plain2)
8484
}
85+
86+
[Fact]
87+
public void CustomNugetJobsWithSamePackageVersionAreGroupedTogether()
88+
{
89+
var job1 = Job.Default.WithNuget("AutoMapper", "7.0.1");
90+
var job2 = Job.Default.WithNuget("AutoMapper", "7.0.1");
91+
92+
var config = ManualConfig.Create(DefaultConfig.Instance)
93+
.With(job1)
94+
.With(job2);
95+
96+
var benchmarks1 = BenchmarkConverter.TypeToBenchmarks(typeof(Plain1), config);
97+
var benchmarks2 = BenchmarkConverter.TypeToBenchmarks(typeof(Plain2), config);
98+
99+
var grouped = benchmarks1.BenchmarksCases.Union(benchmarks2.BenchmarksCases)
100+
.GroupBy(benchmark => benchmark, new BenchmarkPartitioner.BenchmarkRuntimePropertiesComparer())
101+
.ToArray();
102+
103+
Assert.Single(grouped); // 7.0.1
104+
105+
foreach (var grouping in grouped)
106+
Assert.Equal(2 * 3 * 2, grouping.Count()); // ((job1 + job2) * (M1 + M2 + M3) * (Plain1 + Plain2)
107+
}
108+
109+
[Fact]
110+
public void CustomNugetJobsAreGroupedByPackageVersion()
111+
{
112+
var config = ManualConfig.Create(DefaultConfig.Instance)
113+
.With(Job.Default.WithNuget("AutoMapper", "7.0.1"))
114+
.With(Job.Default.WithNuget("AutoMapper", "7.0.0-alpha-0001"));
115+
116+
var benchmarks1 = BenchmarkConverter.TypeToBenchmarks(typeof(Plain1), config);
117+
var benchmarks2 = BenchmarkConverter.TypeToBenchmarks(typeof(Plain2), config);
118+
119+
var grouped = benchmarks1.BenchmarksCases.Union(benchmarks2.BenchmarksCases)
120+
.GroupBy(benchmark => benchmark, new BenchmarkPartitioner.BenchmarkRuntimePropertiesComparer())
121+
.ToArray();
122+
123+
Assert.Equal(2, grouped.Length); // 7.0.1 + 7.0.0-alpha-0001
124+
125+
foreach (var grouping in grouped)
126+
Assert.Equal(3 * 2, grouping.Count()); // (M1 + M2 + M3) * (Plain1 + Plain2)
127+
}
85128
}
86129
}

0 commit comments

Comments
 (0)