Skip to content

Commit

Permalink
Benchmarks Enhancements and Organization (#785)
Browse files Browse the repository at this point in the history
* Fixed broken setup for displaying Benchmark artifacts in Solution Explorer.

* Moved performance tests in the .Tests project over to the .Benchmarks project.

* Fix Nullable errors in Benchmarks

---------

Co-authored-by: Roland Pheasant <[email protected]>
Co-authored-by: Chris Pulman <[email protected]>
  • Loading branch information
3 people authored Dec 7, 2023
1 parent 65bb022 commit e734087
Show file tree
Hide file tree
Showing 11 changed files with 192 additions and 118 deletions.
76 changes: 76 additions & 0 deletions src/DynamicData.Benchmarks/Cache/EditDiff.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;

using BenchmarkDotNet.Attributes;

using DynamicData.Kernel;

namespace DynamicData.Benchmarks.Cache;

[MemoryDiagnoser]
[MarkdownExporterAttribute.GitHub]
public class EditDiff
{
public const int MaxItems
= 1097;

[Benchmark]
[Arguments(7, 3, 5)]
[Arguments(233, 113, MaxItems)]
[Arguments(233, 0, MaxItems)]
[Arguments(233, 233, MaxItems)]
[Arguments(2521, 1187, MaxItems)]
[Arguments(2521, 0, MaxItems)]
[Arguments(2521, 2521, MaxItems)]
[Arguments(5081, 2683, MaxItems)]
[Arguments(5081, 0, MaxItems)]
[Arguments(5081, 5081, MaxItems)]
public void AddsRemovesAndUpdates(int collectionSize, int updateSize, int maxItems)
{
using var subscription = Enumerable
.Range(1, maxItems - 1)
.Select(n => n * (collectionSize - updateSize))
.Select(index => Person.CreateRange(index, updateSize, "Overlap")
.Concat(Person.CreateRange(index + updateSize, collectionSize - updateSize, "Name")))
.Prepend(Person.CreateRange(0, collectionSize, "Name"))
.ToObservable()
.EditDiff(p => p.Id)
.Subscribe();
}

[Benchmark]
[Arguments(7)]
[Arguments(MaxItems)]
public void OptionalAddsAndRemoves(int maxItems)
{
using var subscription = Enumerable
.Range(0, MaxItems)
.Select(n => (n % 2) == 0
? new Person(n, "Name")
: Optional.None<Person>())
.ToObservable()
.EditDiff(p => p.Id)
.Subscribe();
}

private class Person
{
public static IReadOnlyList<Person> CreateRange(int baseId, int count, string baseName)
=> Enumerable
.Range(baseId, count)
.Select(i => new Person(i, baseName + i))
.ToArray();

public Person(int id, string name)
{
Id = id;
Name = name;
}

public int Id { get; }

public string Name { get; }
}
}
8 changes: 4 additions & 4 deletions src/DynamicData.Benchmarks/Cache/SourceCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public BenchmarkItem(int id)
[MarkdownExporterAttribute.GitHub]
public class SourceCache
{
private SourceCache<BenchmarkItem, int> _cache;
private SourceCache<BenchmarkItem, int>? _cache;
private BenchmarkItem[] _items = Enumerable.Range(1, 100).Select(i => new BenchmarkItem(i)).ToArray();

[GlobalSetup]
Expand All @@ -39,18 +39,18 @@ public void Setup()
[IterationSetup]
public void SetupIteration()
{
_cache.Clear();
_cache?.Clear();
_items = Enumerable.Range(1, N).Select(i => new BenchmarkItem(i)).ToArray();
}

[GlobalCleanup]
public void Teardown()
{
_cache.Dispose();
_cache?.Dispose();
_cache = null;
}

[Benchmark]
public void Add() => _cache.AddOrUpdate(_items);
public void Add() => _cache?.AddOrUpdate(_items);
}
}
75 changes: 75 additions & 0 deletions src/DynamicData.Benchmarks/Cache/TransformMany.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;

using BenchmarkDotNet.Attributes;

namespace DynamicData.Benchmarks.Cache;

[MemoryDiagnoser]
[MarkdownExporterAttribute.GitHub]
public class TransformMany
{
[Benchmark]
public void Perf()
{
var children = Enumerable
.Range(1, 10000)
.Select(i => new Child(
id: i,
name: $"Child #{i}"))
.ToArray();

var childIndex = 0;
var parents = Enumerable
.Range(1, 5000)
.Select(i => new Parent(
id: i,
children: new[]
{
children[childIndex++],
children[childIndex++]
}))
.ToArray();

using var source = new SourceCache<Parent, int>(x => x.Id);

using var subscription = source
.Connect()
.TransformMany(p => p.Children, c => c.Name)
.Subscribe();

source.AddOrUpdate(parents);
}

private class Parent
{
public Parent(
int id,
IEnumerable<Child> children)
{
Id = id;
Children = children.ToArray();
}

public int Id { get; }

public IReadOnlyList<Child> Children { get; }
}

private class Child
{
public Child(
int id,
string name)
{
Id = id;
Name = name;
}

public int Id { get; }

public string Name { get; }
}
}
3 changes: 2 additions & 1 deletion src/DynamicData.Benchmarks/DynamicData.Benchmarks.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<OutputType>Exe</OutputType>
<TargetFramework>net6.0-windows</TargetFramework>
<PlatformTarget>AnyCPU</PlatformTarget>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<NoWarn>;1591;1701;1702;1705;CA1822;CA1001</NoWarn>
</PropertyGroup>
Expand All @@ -19,6 +20,6 @@
<ProjectReference Include="..\DynamicData\DynamicData.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="BenchmarkDotNet.Artifacts\" />
<None Include="BenchmarkDotNet.Artifacts\**\*" />
</ItemGroup>
</Project>
12 changes: 6 additions & 6 deletions src/DynamicData.Benchmarks/List/GroupAdd.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ namespace DynamicData.Benchmarks.List
[MarkdownExporterAttribute.GitHub]
public class GroupAdd
{
private IDisposable _groupSubscription;
private SourceList<int> _sourceList;
private IDisposable? _groupSubscription;
private SourceList<int>? _sourceList;
private int[] _items = Enumerable.Range(1, 100).ToArray();

[Params(1, 100, 1_000, 10_000, 100_000)]
Expand All @@ -32,15 +32,15 @@ public void Setup()
[IterationSetup]
public void SetupIteration()
{
_sourceList.Clear();
_sourceList?.Clear();
_items = Enumerable.Range(1, N).ToArray();
}

[GlobalCleanup]
public void Teardown()
{
_groupSubscription.Dispose();
_sourceList.Dispose();
_groupSubscription?.Dispose();
_sourceList?.Dispose();
_sourceList = null;
}

Expand All @@ -52,6 +52,6 @@ public void Teardown()
//}

[Benchmark]
public void AddRange() => _sourceList.AddRange(_items);
public void AddRange() => _sourceList?.AddRange(_items);
}
}
18 changes: 9 additions & 9 deletions src/DynamicData.Benchmarks/List/GroupRemove.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ namespace DynamicData.Benchmarks.List
[MarkdownExporterAttribute.GitHub]
public class GroupRemove
{
private IDisposable _groupSubscription;
private SourceList<int> _sourceList;
private IDisposable? _groupSubscription;
private SourceList<int>? _sourceList;

private static readonly int[] _items = Enumerable.Range(1, 100).ToArray();

Expand All @@ -30,27 +30,27 @@ public void Setup()
[IterationSetup]
public void SetupIteration()
{
_sourceList.AddRange(_items);
_sourceList?.AddRange(_items);
}

[GlobalCleanup]
public void Teardown()
{
_groupSubscription.Dispose();
_sourceList.Dispose();
_groupSubscription?.Dispose();
_sourceList?.Dispose();
_sourceList = null;
}

[Benchmark]
public void RemoveAt() => _sourceList.RemoveAt(1);
public void RemoveAt() => _sourceList?.RemoveAt(1);

[Benchmark]
public void Remove() => _sourceList.RemoveAt(_items[0]);
public void Remove() => _sourceList?.RemoveAt(_items[0]);

[Benchmark]
public void RemoveRange() => _sourceList.RemoveRange(40, 20);
public void RemoveRange() => _sourceList?.RemoveRange(40, 20);

[Benchmark]
public void Clear() => _sourceList.Clear();
public void Clear() => _sourceList?.Clear();
}
}
10 changes: 5 additions & 5 deletions src/DynamicData.Benchmarks/List/SourceList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ namespace DynamicData.Benchmarks.List
[MarkdownExporterAttribute.GitHub]
public class SourceList
{
private SourceList<string> _sourceList;
private string[] _items;
private SourceList<string>? _sourceList;
private string[]? _items;

[Params(1, 100, 1_000, 10_000, 100_000)]
public int N;
Expand All @@ -26,17 +26,17 @@ public class SourceList
[IterationSetup]
public void SetupIteration()
{
_sourceList.Clear();
_sourceList?.Clear();
_items = Enumerable.Range(1, N).Select(i => i.ToString()).ToArray();
}

[GlobalCleanup]
public void Teardown() => _sourceList = null;

[Benchmark]
public void AddRange() => _sourceList.AddRange(_items);
public void AddRange() => _sourceList?.AddRange(_items!);

[Benchmark]
public void Insert() => _sourceList.InsertRange(_items, 0);
public void Insert() => _sourceList?.InsertRange(_items!, 0);
}
}
15 changes: 14 additions & 1 deletion src/DynamicData.Benchmarks/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,25 @@
// Roland Pheasant licenses this file to you under the MIT license.
// See the LICENSE file in the project root for full license information.

using System.IO;
using System.Runtime.CompilerServices;

using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Running;

namespace DynamicData.Benchmarks
{
public static class Program
{
public static void Main(string[] args) => BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);
public static void Main(string[] args)
=> BenchmarkSwitcher
.FromAssembly(typeof(Program).Assembly)
.Run(args, DefaultConfig.Instance
.WithArtifactsPath(Path.Combine(
GetProjectRootDirectory(),
Path.GetFileName(DefaultConfig.Instance.ArtifactsPath))));

private static string GetProjectRootDirectory([CallerFilePath] string? callerFilePath = null)
=> Path.GetDirectoryName(callerFilePath)!;
}
}
37 changes: 0 additions & 37 deletions src/DynamicData.Tests/Cache/EditDiffChangeSetFixture.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reactive.Linq;
using FluentAssertions;
Expand Down Expand Up @@ -118,42 +117,6 @@ public void ResultFailsIfAndOnlyIfSourceFails (bool failSource)
receivedError.Should().Be(failSource ? testException : default);
}

[Trait("Performance", "Manual run only")]
[Theory]
[InlineData(7, 3, 5)]
[InlineData(233, 113, MaxItems)]
[InlineData(233, 0, MaxItems)]
[InlineData(233, 233, MaxItems)]
[InlineData(2521, 1187, MaxItems)]
[InlineData(2521, 0, MaxItems)]
[InlineData(2521, 2521, MaxItems)]
[InlineData(5081, 2683, MaxItems)]
[InlineData(5081, 0, MaxItems)]
[InlineData(5081, 5081, MaxItems)]
public void Perf(int collectionSize, int updateSize, int maxItems)
{
Debug.Assert(updateSize <= collectionSize);

// having
var enumerables = Enumerable.Range(1, maxItems - 1)
.Select(n => n * (collectionSize - updateSize))
.Select(index => CreatePeople(index, updateSize, "Overlap")
.Concat(CreatePeople(index + updateSize, collectionSize - updateSize, "Name")))
.Prepend(CreatePeople(0, collectionSize, "Name"));
var enumObservable = enumerables.ToObservable();

// when
var observableChangeSet = enumObservable.EditDiff(p => p.Id);
using var results = observableChangeSet.AsAggregator();

// then
results.Data.Count.Should().Be(collectionSize);
results.Messages.Count.Should().Be(maxItems);
results.Summary.Overall.Adds.Should().Be((collectionSize * maxItems) - (updateSize * (maxItems - 1)));
results.Summary.Overall.Removes.Should().Be((collectionSize - updateSize) * (maxItems - 1));
results.Summary.Overall.Updates.Should().Be(updateSize * (maxItems - 1));
}

private static Person CreatePerson(int id, string name) => new(id, name);

private static IEnumerable<Person> CreatePeople(int baseId, int count, string baseName) =>
Expand Down
Loading

0 comments on commit e734087

Please sign in to comment.