diff --git a/src/DynamicData.Benchmarks/Cache/Sum_Cache.cs b/src/DynamicData.Benchmarks/Cache/Sum_Cache.cs new file mode 100644 index 000000000..b1aacae6d --- /dev/null +++ b/src/DynamicData.Benchmarks/Cache/Sum_Cache.cs @@ -0,0 +1,108 @@ +using System; +using System.Collections.Generic; +using System.Reactive.Subjects; + +using BenchmarkDotNet.Attributes; + +using DynamicData.Aggregation; + +namespace DynamicData.Benchmarks.Cache; + +[MemoryDiagnoser] +[MarkdownExporterAttribute.GitHub] +public class Sum_Cache +{ + private IReadOnlyList> _addChangeSets = null!; + private IReadOnlyList> _replaceChangeSets = null!; + private IReadOnlyList> _removeChangeSets = null!; + private IReadOnlyList> _refreshChangeSets = null!; + + [Params(100, 500, 1_000, 10_000)] + public int Count { get; set; } + + [GlobalSetup] + public void Setup() + { + var source = new ChangeAwareCache(capacity: Count); + var items = new Item[Count + 1]; + + var addChangeSets = new List>(capacity: Count); + for (var id = 1; id <= Count; ++id) + { + var item = new Item() + { + Id = id, + Value = id + }; + items[id] = item; + source.Add(item, key: id); + addChangeSets.Add(source.CaptureChanges()); + } + _addChangeSets = addChangeSets; + + var replaceChangeSets = new List>(capacity: Count); + for (var id = 1; id <= Count; ++id) + { + var replacement = new Item() + { + Id = id, + Value = id * 2 + }; + items[id] = replacement; + source.AddOrUpdate(replacement, key: id); + replaceChangeSets.Add(source.CaptureChanges()); + } + _replaceChangeSets = replaceChangeSets; + + var refreshChangeSets = new List>(capacity: Count); + for (var id = 1; id <= Count; ++id) + { + // Mutate in place, then refresh - the scenario stateless aggregation cannot currently observe. + items[id].Value += 1; + source.Refresh(id); + refreshChangeSets.Add(source.CaptureChanges()); + } + _refreshChangeSets = refreshChangeSets; + + var removeChangeSets = new List>(capacity: Count); + for (var id = 1; id <= Count; ++id) + { + source.Remove(id); + removeChangeSets.Add(source.CaptureChanges()); + } + _removeChangeSets = removeChangeSets; + } + + [Benchmark] + public void Adds() => Run(_addChangeSets); + + [Benchmark] + public void Replaces() => Run(_replaceChangeSets); + + [Benchmark] + public void Refreshes() => Run(_refreshChangeSets); + + [Benchmark] + public void Removes() => Run(_removeChangeSets); + + private static void Run(IReadOnlyList> changeSets) + { + using var source = new Subject>(); + + using var subscription = source + .Sum(static item => item.Value) + .Subscribe(); + + foreach (var changeSet in changeSets) + source.OnNext(changeSet); + + source.OnCompleted(); + } + + private sealed class Item + { + public required int Id { get; init; } + + public int Value { get; set; } + } +} diff --git a/src/DynamicData.Benchmarks/List/Sum_List.cs b/src/DynamicData.Benchmarks/List/Sum_List.cs new file mode 100644 index 000000000..74aa18f4c --- /dev/null +++ b/src/DynamicData.Benchmarks/List/Sum_List.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using System.Reactive.Subjects; + +using BenchmarkDotNet.Attributes; + +using DynamicData.Aggregation; + +namespace DynamicData.Benchmarks.List; + +[MemoryDiagnoser] +[MarkdownExporterAttribute.GitHub] +public class Sum_List +{ + private IReadOnlyList> _addChangeSets = null!; + private IReadOnlyList> _replaceChangeSets = null!; + private IReadOnlyList> _removeChangeSets = null!; + private IReadOnlyList> _refreshChangeSets = null!; + + [Params(100, 500, 1_000, 10_000)] + public int Count { get; set; } + + [GlobalSetup] + public void Setup() + { + var source = new ChangeAwareList(capacity: Count); + + var addChangeSets = new List>(capacity: Count); + for (var id = 1; id <= Count; ++id) + { + source.Add(new Item() + { + Id = id, + Value = id + }); + addChangeSets.Add(source.CaptureChanges()); + } + _addChangeSets = addChangeSets; + + var replaceChangeSets = new List>(capacity: Count); + for (var index = 0; index < Count; ++index) + { + source[index] = new Item() + { + Id = index + 1, + Value = (index + 1) * 2 + }; + replaceChangeSets.Add(source.CaptureChanges()); + } + _replaceChangeSets = replaceChangeSets; + + var refreshChangeSets = new List>(capacity: Count); + for (var index = 0; index < Count; ++index) + { + // Mutate in place, then refresh - the scenario stateless aggregation cannot currently observe. + source[index].Value += 1; + source.RefreshAt(index); + refreshChangeSets.Add(source.CaptureChanges()); + } + _refreshChangeSets = refreshChangeSets; + + var removeChangeSets = new List>(capacity: Count); + for (var id = 1; id <= Count; ++id) + { + source.RemoveAt(source.Count - 1); + removeChangeSets.Add(source.CaptureChanges()); + } + _removeChangeSets = removeChangeSets; + } + + [Benchmark] + public void Adds() => Run(_addChangeSets); + + [Benchmark] + public void Replaces() => Run(_replaceChangeSets); + + [Benchmark] + public void Refreshes() => Run(_refreshChangeSets); + + [Benchmark] + public void Removes() => Run(_removeChangeSets); + + private static void Run(IReadOnlyList> changeSets) + { + using var source = new Subject>(); + + using var subscription = source + .Sum(static item => item.Value) + .Subscribe(); + + foreach (var changeSet in changeSets) + source.OnNext(changeSet); + + source.OnCompleted(); + } + + private sealed class Item + { + public required int Id { get; init; } + + public int Value { get; set; } + } +}