Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,096 changes: 1,337 additions & 759 deletions .editorconfig

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Keep analyzer-sensitive source and build files on LF across all checkout platforms.
*.cs text eol=lf
*.csproj text eol=lf
*.props text eol=lf
*.targets text eol=lf
*.slnx text eol=lf
.editorconfig text eol=lf
.gitattributes text eol=lf
10 changes: 6 additions & 4 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -75,21 +75,23 @@
<!-- Alias Lock to the dedicated System.Threading.Lock on .NET 9+ (faster EnterScope fast path),
and to a plain object elsewhere (the lock statement falls back to Monitor). -->
<ItemGroup Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net9.0'))">
<Using Include="System.Threading.Lock" Alias="Lock"/>
<Using Include="System.Threading.Lock" Alias="Lock" />
</ItemGroup>
<ItemGroup Condition="!$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net9.0'))">
<Using Include="System.Object" Alias="Lock"/>
<Using Include="System.Object" Alias="Lock" />
</ItemGroup>

<ItemGroup>
<None Include="$(MSBuildThisFileDirectory)images\logo.png" Pack="true" PackagePath="\" />
<None Include="$(MSBuildThisFileDirectory)LICENSE" Pack="true" PackagePath="LICENSE" />
<None Include="$(MSBuildThisFileDirectory)README.md" Pack="true" PackagePath="\"/>
<None Include="$(MSBuildThisFileDirectory)README.md" Pack="true" PackagePath="\" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="MinVer" PrivateAssets="all" />
<PackageReference Include="StyleSharp.Analyzers" PrivateAssets="All" />
<PackageReference Include="StyleSharp.Analyzers" PrivateAssets="all" />
<PackageReference Include="PerformanceSharp.Analyzers" PrivateAssets="all" />
<PackageReference Include="SecuritySharp.Analyzers" PrivateAssets="all" />
<PackageReference Include="Roslynator.Analyzers" PrivateAssets="All" />
</ItemGroup>
</Project>
6 changes: 4 additions & 2 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@
<PackageVersion Include="Microsoft.Testing.Extensions.CodeCoverage" Version="18.9.0" />
<PackageVersion Include="MinVer" Version="7.0.0" />
<PackageVersion Include="Nuke.Common" Version="10.1.0" />
<PackageVersion Include="ReactiveUI.Primitives" Version="6.0.0" />
<PackageVersion Include="ReactiveUI.Primitives" Version="7.1.0" />
<PackageVersion Include="ReactiveUI.Primitives.Reactive" Version="7.1.0" />
<PackageVersion Include="ReactiveUI.Primitives.Wpf" Version="6.0.0" />
<PackageVersion Include="ReactiveUI.Primitives.Wpf" Version="7.1.0" />
<PackageVersion Include="Roslynator.Analyzers" Version="4.15.0" />
<PackageVersion Include="StyleSharp.Analyzers" Version="3.40.0" />
<PackageVersion Include="PerformanceSharp.Analyzers" Version="3.40.0" />
<PackageVersion Include="SecuritySharp.Analyzers" Version="3.40.0" />
<PackageVersion Include="System.Buffers" Version="4.6.1" />
<PackageVersion Include="System.Memory" Version="4.6.3" />
<PackageVersion Include="System.Security.Cryptography.Xml" Version="10.0.10" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ internal static class BenchmarkObservableExtensions
/// <summary>Subscribes an action observer.</summary>
/// <param name="onNext">The next-value handler.</param>
/// <returns>The subscription disposable.</returns>
public IDisposable SubscribeObserver(Action<T> onNext)
internal IDisposable SubscribeObserver(Action<T> onNext)
{
ArgumentNullException.ThrowIfNull(source);
ArgumentNullException.ThrowIfNull(onNext);
Expand Down
60 changes: 36 additions & 24 deletions src/ReactiveList.Benchmarks/DynamicDataParityBenchmarks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,38 @@

namespace ReactiveList.Benchmarks;

/// <summary>Provides DynamicDataParityBenchmarks.</summary>
/// <summary>Benchmarks ReactiveList and DynamicData pipelines with equivalent observable behavior.</summary>
[MemoryDiagnoser]
public class DynamicDataParityBenchmarks
{
/// <summary>Identifies the even values used by parity-based pipelines.</summary>
private const int ParityDivisor = 2;

/// <summary>Scales values emitted by transform benchmarks.</summary>
private const int TransformMultiplier = 2;

/// <summary>Stores the sequential values supplied to each benchmark invocation.</summary>
private int[] _data = [];

/// <summary>Gets or sets the item count.</summary>
/// <summary>Gets or sets the number of sequential items supplied to each benchmark.</summary>
[Params(1_000, 10_000)]
public int Count { get; set; }

/// <summary>Provides Setup.</summary>
/// <summary>Initializes the sequential source data for the configured item count.</summary>
[GlobalSetup]
public void Setup() => _data = Enumerable.Range(0, Count).ToArray();
public void Setup()
{
var data = new int[Count];
for (var index = 0; index < data.Length; index++)
{
data[index] = index;
}

_data = data;
}

/// <summary>Provides ReactiveList_Connect_Preloaded_InitialSnapshot.</summary>
/// <returns>The result.</returns>
/// <summary>Benchmarks the initial snapshot delivered when connecting to a preloaded ReactiveList.</summary>
/// <returns>The number of changes reported by the observer.</returns>
[Benchmark]
public int ReactiveList_Connect_Preloaded_InitialSnapshot()
{
Expand All @@ -41,8 +53,8 @@ public int ReactiveList_Connect_Preloaded_InitialSnapshot()
return total;
}

/// <summary>Provides SourceList_Connect_Preloaded_InitialSnapshot.</summary>
/// <returns>The result.</returns>
/// <summary>Benchmarks the initial snapshot delivered when connecting to a preloaded SourceList.</summary>
/// <returns>The number of changes reported by the observer.</returns>
[Benchmark]
public int SourceList_Connect_Preloaded_InitialSnapshot()
{
Expand All @@ -53,14 +65,14 @@ public int SourceList_Connect_Preloaded_InitialSnapshot()
return total;
}

/// <summary>Provides ReactiveList_FilterTransformSort.</summary>
/// <returns>The result.</returns>
/// <summary>Benchmarks filtering, transforming, and sorting a ReactiveList change stream.</summary>
/// <returns>The number of changes delivered by the pipeline.</returns>
[Benchmark]
public int ReactiveList_FilterTransformSort()
{
using var list = new ReactiveList<int>();
var total = 0;
var pipeline = CP.Primitives.ReactiveListExtensions.SortBy<int, int>(
var pipeline = CP.Primitives.ReactiveListExtensions.SortBy(
list.Connect()
.WhereChanges(static change => change.Current % ParityDivisor == 0)
.SelectChanges(static item => item * TransformMultiplier),
Expand All @@ -71,8 +83,8 @@ public int ReactiveList_FilterTransformSort()
return total;
}

/// <summary>Provides SourceList_FilterTransformSortBind.</summary>
/// <returns>The result.</returns>
/// <summary>Benchmarks filtering, transforming, sorting, and binding a SourceList change stream.</summary>
/// <returns>The number of values in the bound collection.</returns>
[Benchmark]
public int SourceList_FilterTransformSortBind()
{
Expand All @@ -82,14 +94,14 @@ public int SourceList_FilterTransformSortBind()
.Transform(static item => item * TransformMultiplier)
.Sort(SortExpressionComparer<int>.Ascending(static item => item))
.Bind(out ReadOnlyObservableCollection<int> bound)
.SubscribeObserver(_ => { });
.SubscribeObserver(static _ => { });

list.AddRange(_data);
return bound.Count;
}

/// <summary>Provides ReactiveList_INCC_AddRange_WithItemsSubscriber.</summary>
/// <returns>The result.</returns>
/// <summary>Benchmarks item collection-change events raised by a ReactiveList add range.</summary>
/// <returns>The number of collection-change events raised.</returns>
[Benchmark]
public int ReactiveList_INCC_AddRange_WithItemsSubscriber()
{
Expand All @@ -101,24 +113,24 @@ public int ReactiveList_INCC_AddRange_WithItemsSubscriber()
return events;
}

/// <summary>Provides SourceList_INCC_AddRange_WithBoundSubscriber.</summary>
/// <returns>The result.</returns>
/// <summary>Benchmarks bound collection-change events raised by a SourceList add range.</summary>
/// <returns>The number of collection-change events raised.</returns>
[Benchmark]
public int SourceList_INCC_AddRange_WithBoundSubscriber()
{
using var list = new SourceList<int>();
using var subscription = list.Connect()
.Bind(out ReadOnlyObservableCollection<int> bound)
.SubscribeObserver(_ => { });
.SubscribeObserver(static _ => { });
var events = 0;
((INotifyCollectionChanged)bound).CollectionChanged += (_, _) => events++;

list.AddRange(_data);
return events;
}

/// <summary>Provides QuaternaryList_Stream_AddRange_DeliveryWait.</summary>
/// <returns>The result.</returns>
/// <summary>Benchmarks delivery of a QuaternaryList stream notification for an add range.</summary>
/// <returns>The number of stream notifications received.</returns>
[Benchmark]
public int QuaternaryList_Stream_AddRange_DeliveryWait()
{
Expand All @@ -133,12 +145,12 @@ public int QuaternaryList_Stream_AddRange_DeliveryWait()
});

list.AddRange(_data);
delivered.Wait(TimeSpan.FromSeconds(1));
_ = delivered.Wait(TimeSpan.FromSeconds(1));
return events;
}

/// <summary>Provides SourceList_Stream_AddRange_Delivery.</summary>
/// <returns>The result.</returns>
/// <summary>Benchmarks delivery of a SourceList change stream notification for an add range.</summary>
/// <returns>The number of change stream notifications received.</returns>
[Benchmark]
public int SourceList_Stream_AddRange_Delivery()
{
Expand Down
Loading