Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New SortAndBind operator #878

Merged
merged 11 commits into from
Mar 20, 2024
62 changes: 62 additions & 0 deletions src/DynamicData.Benchmarks/Cache/BindAndSortInitial.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
using BenchmarkDotNet.Attributes;
using System;
using System.Linq;
using System.Reactive.Disposables;
using System.Reactive.Subjects;
using DynamicData.Binding;

namespace DynamicData.Benchmarks.Cache;

[MemoryDiagnoser]
[MarkdownExporterAttribute.GitHub]
public class BindAndSortInitial: IDisposable
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BindAndSort_Initial to match BindAndSort_Change?

{
private readonly Random _random = new();

private record Item(string Name, int Id, int Ranking);

private readonly SortExpressionComparer<Item> _comparer = SortExpressionComparer<Item>.Ascending(i => i.Ranking).ThenByAscending(i => i.Name);


Subject<IChangeSet<Item, int>> _oldSubject = new();
Subject<IChangeSet<Item, int>> _newSubject = new();

private IDisposable? _cleanUp;
private ChangeSet<Item, int>? _changeSet;


[Params(10, 100, 1_000, 10_000, 50_000)]
public int Count { get; set; }


[GlobalSetup]
public void SetUp()
{
_oldSubject = new Subject<IChangeSet<Item, int>>();
_newSubject = new Subject<IChangeSet<Item, int>>();

var changeSet = new ChangeSet<Item, int>(Count);
foreach (var i in Enumerable.Range(1, Count))
{
var item = new Item($"Item{i}", i, _random.Next(1, 1000));
changeSet.Add(new Change<Item, int>(ChangeReason.Add, i, item));
}

_changeSet = changeSet;

_cleanUp = new CompositeDisposable
(
_newSubject.BindAndSort(out var list1, _comparer).Subscribe(),
_oldSubject.Sort(_comparer).Bind(out var list2).Subscribe()
);
}


[Benchmark(Baseline = true)]
public void Old() => _oldSubject.OnNext(_changeSet!);

[Benchmark]
public void New() => _newSubject.OnNext(_changeSet!);

public void Dispose() => _cleanUp?.Dispose();
}
95 changes: 95 additions & 0 deletions src/DynamicData.Benchmarks/Cache/BindAndSort_Change.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive.Disposables;
using System.Reactive.Subjects;
using BenchmarkDotNet.Attributes;
using DynamicData.Binding;

namespace DynamicData.Benchmarks.Cache;

[MemoryDiagnoser]
[MarkdownExporterAttribute.GitHub]
public class BindAndSortChange: IDisposable
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Class name doesn't match file name.

{
private readonly Random _random = new();
private record Item(string Name, int Id, int Ranking);

private readonly SortExpressionComparer<Item> _comparer = SortExpressionComparer<Item>
.Ascending(i => i.Ranking)
.ThenByAscending(i => i.Name);

Subject<IChangeSet<Item, int>> _oldSubject = new();
Subject<IChangeSet<Item, int>> _newSubject = new();

private IDisposable? _cleanUp;

private ReadOnlyObservableCollection<Item>? _newList;
private ReadOnlyObservableCollection<Item>? _oldList;


[Params(10, 100, 1_000, 10_000, 50_000)]
public int Count { get; set; }


[GlobalSetup]
public void SetUp()
{
_oldSubject = new Subject<IChangeSet<Item, int>>();
_newSubject = new Subject<IChangeSet<Item, int>>();

var options = BindAndSortOptions.Default with
{
InitialCapacity = Count,
UseBinarySearch = true
};

_cleanUp = new CompositeDisposable
(
_newSubject.BindAndSort(out var list1, _comparer).Subscribe(),
_oldSubject.Sort(_comparer).Bind(out var list2).Subscribe()
);

_newList = list1;
_oldList = list2;



var changeSet = new ChangeSet<Item, int>(Count);
foreach (var i in Enumerable.Range(1, Count))
{
var item = new Item($"Item{i}", i, _random.Next(1, 1000));
changeSet.Add(new Change<Item, int>(ChangeReason.Add, i, item));
}

_newSubject.OnNext(changeSet);
_oldSubject.OnNext(changeSet);

}

[Benchmark(Baseline = true)]
public void Old()
{
var original = _oldList![Count / 2];
var updated = original with { Ranking = _random.Next(1, 1000) };

_oldSubject.OnNext(new ChangeSet<Item, int>
{
new(ChangeReason.Update, original.Id, updated, original)
});
}

[Benchmark]
public void New()
{
var original = _newList![Count / 2];
var updated = original with { Ranking = _random.Next(1, 1000) };

_newSubject.OnNext(new ChangeSet<Item, int>
{
new(ChangeReason.Update, original.Id, updated, original)
});
}

public void Dispose() => _cleanUp?.Dispose();
}
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,16 @@ namespace DynamicData.Binding
[System.Obsolete("This never worked properly in the first place")]
public System.IDisposable SuspendNotifications(bool invokePropertyChangeEventWhenDisposed = true) { }
}
public struct BindAndSortOptions : System.IEquatable<DynamicData.Binding.BindAndSortOptions>
{
public static readonly DynamicData.Binding.BindAndSortOptions Default;
public BindAndSortOptions(int ResetThreshold, bool UseReplaceForUpdates, bool ResetOnFirstTimeLoad, bool UseBinarySearch, int InitialCapacity) { }
public int InitialCapacity { get; set; }
public bool ResetOnFirstTimeLoad { get; set; }
public int ResetThreshold { get; set; }
public bool UseBinarySearch { get; set; }
public bool UseReplaceForUpdates { get; set; }
}
public class BindingListAdaptor<T> : DynamicData.IChangeSetAdaptor<T>
where T : notnull
{
Expand Down Expand Up @@ -1189,6 +1199,18 @@ namespace DynamicData
public static System.IObservable<DynamicData.IChangeSet<TObject, TKey>> Bind<TObject, TKey>(this System.IObservable<DynamicData.ISortedChangeSet<TObject, TKey>> source, out System.Collections.ObjectModel.ReadOnlyObservableCollection<TObject> readOnlyObservableCollection, int resetThreshold = 25, bool useReplaceForUpdates = true, DynamicData.Binding.ISortedObservableCollectionAdaptor<TObject, TKey>? adaptor = null)
where TObject : notnull
where TKey : notnull { }
public static System.IObservable<DynamicData.IChangeSet<TObject, TKey>> BindAndSort<TObject, TKey>(this System.IObservable<DynamicData.IChangeSet<TObject, TKey>> source, System.Collections.Generic.IList<TObject> targetList, System.Collections.Generic.IComparer<TObject> comparer)
where TObject : notnull
where TKey : notnull { }
public static System.IObservable<DynamicData.IChangeSet<TObject, TKey>> BindAndSort<TObject, TKey>(this System.IObservable<DynamicData.IChangeSet<TObject, TKey>> source, out System.Collections.ObjectModel.ReadOnlyObservableCollection<TObject> readOnlyObservableCollection, System.Collections.Generic.IComparer<TObject> comparer)
where TObject : notnull
where TKey : notnull { }
public static System.IObservable<DynamicData.IChangeSet<TObject, TKey>> BindAndSort<TObject, TKey>(this System.IObservable<DynamicData.IChangeSet<TObject, TKey>> source, System.Collections.Generic.IList<TObject> targetList, System.Collections.Generic.IComparer<TObject> comparer, DynamicData.Binding.BindAndSortOptions options)
where TObject : notnull
where TKey : notnull { }
public static System.IObservable<DynamicData.IChangeSet<TObject, TKey>> BindAndSort<TObject, TKey>(this System.IObservable<DynamicData.IChangeSet<TObject, TKey>> source, out System.Collections.ObjectModel.ReadOnlyObservableCollection<TObject> readOnlyObservableCollection, System.Collections.Generic.IComparer<TObject> comparer, DynamicData.Binding.BindAndSortOptions options)
where TObject : notnull
where TKey : notnull { }
public static System.IObservable<DynamicData.IChangeSet<TObject, TKey>> BufferInitial<TObject, TKey>(this System.IObservable<DynamicData.IChangeSet<TObject, TKey>> source, System.TimeSpan initialBuffer, System.Reactive.Concurrency.IScheduler? scheduler = null)
where TObject : notnull
where TKey : notnull { }
Expand Down
Loading