From ac329e4962c7a63341984d5a5ac8b719cd87e82f Mon Sep 17 00:00:00 2001 From: RLittlesII Date: Wed, 15 Aug 2018 01:39:44 -0500 Subject: [PATCH] Added Benchmark Harness - Added NavigationStack Benchmarks - Added AutoPersist Benchmarks - Added ReactiveList Operations Benchmarks - Added RoutableViewModelMixin Benchmarks --- benchmarks/ReactiveUI.Benchmarks.sln | 39 ++++++++++ .../AutoPersistBenchmark.cs | 31 ++++++++ .../CreateReactiveListBenchmark.cs | 47 +++++++++++ benchmarks/ReactiveUI.Benchmarks/Harness.cs | 45 +++++++++++ .../INPCObservableForPropertyBenchmarks.cs | 78 +++++++++++++++++++ .../Mocks/MockHostScreen.cs | 7 ++ .../Mocks/MockViewModel.cs | 10 +++ .../NavigationStackBenchmark.cs | 53 +++++++++++++ .../ReactiveListOperationBenchmark.cs | 61 +++++++++++++++ .../ReactiveUI.Benchmarks.csproj | 19 +++++ .../RoutableViewModelMixinsBenchmarks.cs | 54 +++++++++++++ 11 files changed, 444 insertions(+) create mode 100644 benchmarks/ReactiveUI.Benchmarks.sln create mode 100644 benchmarks/ReactiveUI.Benchmarks/AutoPersistBenchmark.cs create mode 100644 benchmarks/ReactiveUI.Benchmarks/CreateReactiveListBenchmark.cs create mode 100644 benchmarks/ReactiveUI.Benchmarks/Harness.cs create mode 100644 benchmarks/ReactiveUI.Benchmarks/INPCObservableForPropertyBenchmarks.cs create mode 100644 benchmarks/ReactiveUI.Benchmarks/Mocks/MockHostScreen.cs create mode 100644 benchmarks/ReactiveUI.Benchmarks/Mocks/MockViewModel.cs create mode 100644 benchmarks/ReactiveUI.Benchmarks/NavigationStackBenchmark.cs create mode 100644 benchmarks/ReactiveUI.Benchmarks/ReactiveListOperationBenchmark.cs create mode 100644 benchmarks/ReactiveUI.Benchmarks/ReactiveUI.Benchmarks.csproj create mode 100644 benchmarks/ReactiveUI.Benchmarks/RoutableViewModelMixinsBenchmarks.cs diff --git a/benchmarks/ReactiveUI.Benchmarks.sln b/benchmarks/ReactiveUI.Benchmarks.sln new file mode 100644 index 0000000000..cf31995139 --- /dev/null +++ b/benchmarks/ReactiveUI.Benchmarks.sln @@ -0,0 +1,39 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27703.2000 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReactiveUI.Benchmarks", "ReactiveUI.Benchmarks\ReactiveUI.Benchmarks.csproj", "{EC6DD0F1-4D99-4BE8-B3F1-1DD71D86C24A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmarks", "benchmarks", "{0DB2C65F-BE8F-4021-8860-D2D6EB9DD80A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{CB287F1A-1E15-40D7-85F5-F2A97A944B57}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReactiveUI", "..\src\ReactiveUI\ReactiveUI.csproj", "{1BB9FAA2-4E90-4E93-8B77-434DF10166ED}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {EC6DD0F1-4D99-4BE8-B3F1-1DD71D86C24A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EC6DD0F1-4D99-4BE8-B3F1-1DD71D86C24A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EC6DD0F1-4D99-4BE8-B3F1-1DD71D86C24A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EC6DD0F1-4D99-4BE8-B3F1-1DD71D86C24A}.Release|Any CPU.Build.0 = Release|Any CPU + {1BB9FAA2-4E90-4E93-8B77-434DF10166ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1BB9FAA2-4E90-4E93-8B77-434DF10166ED}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1BB9FAA2-4E90-4E93-8B77-434DF10166ED}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1BB9FAA2-4E90-4E93-8B77-434DF10166ED}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {EC6DD0F1-4D99-4BE8-B3F1-1DD71D86C24A} = {0DB2C65F-BE8F-4021-8860-D2D6EB9DD80A} + {1BB9FAA2-4E90-4E93-8B77-434DF10166ED} = {CB287F1A-1E15-40D7-85F5-F2A97A944B57} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {A602807A-CF55-4092-A5CD-646572A09B88} + EndGlobalSection +EndGlobal diff --git a/benchmarks/ReactiveUI.Benchmarks/AutoPersistBenchmark.cs b/benchmarks/ReactiveUI.Benchmarks/AutoPersistBenchmark.cs new file mode 100644 index 0000000000..362dc96a5a --- /dev/null +++ b/benchmarks/ReactiveUI.Benchmarks/AutoPersistBenchmark.cs @@ -0,0 +1,31 @@ +using System; +using System.Reactive; +using System.Reactive.Linq; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Attributes.Exporters; +using BenchmarkDotNet.Attributes.Jobs; + +namespace ReactiveUI.Benchmarks +{ + [CoreJob] + [MarkdownExporterAttribute.GitHub] + public class AutoPersistBenchmark + { + private ReactiveList _collection; + + [GlobalSetup] + public void Setup() + { + _collection = new ReactiveList(new[] + { + "ReactiveUI", + "ReactiveUI.XamForms", + "ReactiveUI.WPF", + "ReactiveUI.Events" + }); + } + + [Benchmark] + public void AutoPersistCollection() => _collection.AutoPersist(x => Observable.Return(Unit.Default), TimeSpan.FromMilliseconds(200)); + } +} diff --git a/benchmarks/ReactiveUI.Benchmarks/CreateReactiveListBenchmark.cs b/benchmarks/ReactiveUI.Benchmarks/CreateReactiveListBenchmark.cs new file mode 100644 index 0000000000..377fde9f26 --- /dev/null +++ b/benchmarks/ReactiveUI.Benchmarks/CreateReactiveListBenchmark.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Attributes.Exporters; +using BenchmarkDotNet.Attributes.Jobs; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Toolchains.CsProj; +using DynamicData; + +namespace ReactiveUI.Benchmarks +{ + [CoreJob] + [MarkdownExporterAttribute.GitHub] + public class CreateReactiveListBenchmark + { + [Benchmark(Baseline = true)] + public object CreateList() => new List(); + + [Benchmark] + public object CreateObservableCollection() => new ObservableCollection(); + + [Benchmark] + public object CreateReactiveList() => new ReactiveList(); + + [Benchmark] + public object CreateReactiveListFromList() => new ReactiveList(new string[] + { + "ReactiveUI", + "ReactiveUI.XamForms", + "ReactiveUI.WPF", + "ReactiveUI.Events", + "ReactiveUI", + "ReactiveUI.XamForms", + "ReactiveUI.WPF", + "ReactiveUI.Events" + }); + + [Benchmark] + public object CreateReadOnlyObservableList() => + new ReadOnlyObservableCollection(new ObservableCollection(Enumerable.Empty())); + + [Benchmark] + public object CreateSourceList() => new SourceList().Connect().AsObservableList(); + } +} diff --git a/benchmarks/ReactiveUI.Benchmarks/Harness.cs b/benchmarks/ReactiveUI.Benchmarks/Harness.cs new file mode 100644 index 0000000000..3c8d93633c --- /dev/null +++ b/benchmarks/ReactiveUI.Benchmarks/Harness.cs @@ -0,0 +1,45 @@ +using System; +using BenchmarkDotNet.Running; +using Xunit; + +namespace ReactiveUI.Benchmarks +{ + public class Harness + { + [Fact] + public void Auto_Persist_Benchmark() + { + BenchmarkRunner.Run(); + } + + [Fact] + public void Create_Reactive_List_Benchmark() + { + BenchmarkRunner.Run(); + } + + [Fact] + public void Reactive_List_Operation_Benchmark() + { + BenchmarkRunner.Run(); + } + + [Fact] + public void Navigation_Reactive_List_Benchmark() + { + BenchmarkRunner.Run(); + } + + [Fact] + public void Routable_View_Model_Mixin_Benchmark() + { + BenchmarkRunner.Run(); + } + + [Fact] + public void INPC_Observable_For_Property_Benchmarks() + { + BenchmarkRunner.Run(); + } + } +} diff --git a/benchmarks/ReactiveUI.Benchmarks/INPCObservableForPropertyBenchmarks.cs b/benchmarks/ReactiveUI.Benchmarks/INPCObservableForPropertyBenchmarks.cs new file mode 100644 index 0000000000..624740d052 --- /dev/null +++ b/benchmarks/ReactiveUI.Benchmarks/INPCObservableForPropertyBenchmarks.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq.Expressions; +using System.Runtime.CompilerServices; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Attributes.Columns; +using BenchmarkDotNet.Attributes.Exporters; +using BenchmarkDotNet.Attributes.Jobs; + +namespace ReactiveUI.Benchmarks +{ + [ClrJob] + [CoreJob] + [MonoJob] + [RPlotExporter] + [RankColumn] + [MarkdownExporterAttribute.GitHub] + public class INPCObservableForPropertyBenchmarks + { + private Expression exp; + private readonly Expression> expr = x => x.Property1; + private readonly INPCObservableForProperty instance = new INPCObservableForProperty(); + private string propertyName; + + + [GlobalSetup] + public void Setup() + { + exp = Reflection.Rewrite(expr.Body); + propertyName = exp.GetMemberInfo().Name; + } + + [Benchmark] + public void PropertyBinding() + { + var testClass = new TestClassChanged(); + + var changes = new List>(); + instance.GetNotificationForProperty(testClass, exp, propertyName, false).Subscribe(c => changes.Add(c)); + } + + private class TestClassChanged : INotifyPropertyChanged + { + private string property; + + private string property2; + + public string Property1 + { + get => property; + set + { + property = value; + OnPropertyChanged(); + } + } + + public string Property2 + { + get => property2; + set + { + property2 = value; + OnPropertyChanged(); + } + } + + public event PropertyChangedEventHandler PropertyChanged; + + public void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + var handler = PropertyChanged; + if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); + } + } + } +} diff --git a/benchmarks/ReactiveUI.Benchmarks/Mocks/MockHostScreen.cs b/benchmarks/ReactiveUI.Benchmarks/Mocks/MockHostScreen.cs new file mode 100644 index 0000000000..cde54266ae --- /dev/null +++ b/benchmarks/ReactiveUI.Benchmarks/Mocks/MockHostScreen.cs @@ -0,0 +1,7 @@ +namespace ReactiveUI.Benchmarks +{ + public class MockHostScreen : IScreen + { + public RoutingState Router { get; } = new RoutingState(); + } +} \ No newline at end of file diff --git a/benchmarks/ReactiveUI.Benchmarks/Mocks/MockViewModel.cs b/benchmarks/ReactiveUI.Benchmarks/Mocks/MockViewModel.cs new file mode 100644 index 0000000000..cef8343b3f --- /dev/null +++ b/benchmarks/ReactiveUI.Benchmarks/Mocks/MockViewModel.cs @@ -0,0 +1,10 @@ +namespace ReactiveUI.Benchmarks +{ + public class MockViewModel : ReactiveObject, IRoutableViewModel + { + public IScreen HostScreen { get; } + public string UrlPathSegment { get; } + + public MockViewModel() => HostScreen = new MockHostScreen(); + } +} diff --git a/benchmarks/ReactiveUI.Benchmarks/NavigationStackBenchmark.cs b/benchmarks/ReactiveUI.Benchmarks/NavigationStackBenchmark.cs new file mode 100644 index 0000000000..b871f35b32 --- /dev/null +++ b/benchmarks/ReactiveUI.Benchmarks/NavigationStackBenchmark.cs @@ -0,0 +1,53 @@ +using System; +using System.Linq; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Attributes.Exporters; +using BenchmarkDotNet.Attributes.Jobs; + +namespace ReactiveUI.Benchmarks +{ + [CoreJob] + [MarkdownExporterAttribute.GitHub] + public class NavigationStackBenchmark + { + private Func _mockViewModel; + private RoutingState _router; + + [GlobalCleanup] + public void Cleanup() + { + _router = null; + _mockViewModel = null; + } + + [Benchmark] + public object Navigate() => _router.Navigate.Execute(_mockViewModel()).Subscribe(); + + [Benchmark] + public object NavigateAndReset() => _router.NavigateAndReset.Execute(_mockViewModel()).Subscribe(); + + [Benchmark] + public object NavigateBack() + { + _router.NavigateAndReset.Execute(_mockViewModel()).Subscribe(); + return _router.NavigateBack.Execute().Subscribe(); + } + + [Benchmark] + public object NavigationStack() + { + _router.NavigateAndReset.Execute(_mockViewModel()).Subscribe(); + return _router.NavigationStack.ToList(); + } + + [Benchmark] + public object RoutingState() => new RoutingState(); + + [GlobalSetup] + public void Setup() + { + _router = new RoutingState(); + _mockViewModel = () => new MockViewModel(); + } + } +} diff --git a/benchmarks/ReactiveUI.Benchmarks/ReactiveListOperationBenchmark.cs b/benchmarks/ReactiveUI.Benchmarks/ReactiveListOperationBenchmark.cs new file mode 100644 index 0000000000..3be65e3e0a --- /dev/null +++ b/benchmarks/ReactiveUI.Benchmarks/ReactiveListOperationBenchmark.cs @@ -0,0 +1,61 @@ +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Attributes.Exporters; +using BenchmarkDotNet.Attributes.Jobs; +using DynamicData; + +namespace ReactiveUI.Benchmarks +{ + [CoreJob] + [MarkdownExporterAttribute.GitHub] + public class ReactiveListOperationBenchmark + { + private ReactiveList _reactiveList; + + [GlobalSetup] + public void Setup() + { + _reactiveList = new ReactiveList(); + } + + [GlobalCleanup] + public void Teardown() + { + _reactiveList = null; + } + + [Benchmark] + public void Add() => _reactiveList.Add("ReactiveUI.Fody"); + + [Benchmark] + public void AddRange() => _reactiveList.AddRange(new[] + { + "ReactiveUI", + "ReactiveUI.XamForms", + "ReactiveUI.WPF", + "ReactiveUI.Events", + "ReactiveUI", + "ReactiveUI.XamForms", + "ReactiveUI.WPF", + "ReactiveUI.Events" + }); + + [Benchmark] + public void AddOrInsertRange() => _reactiveList.AddOrInsertRange(new[] + { + "ReactiveUI", + "ReactiveUI.XamForms", + "ReactiveUI.WPF", + "ReactiveUI.Events", + "ReactiveUI", + "ReactiveUI.XamForms", + "ReactiveUI.WPF", + "ReactiveUI.Events" + }, -1); + + [Benchmark] + public void Insert() => _reactiveList.Insert(1, "ReactiveUI.Benchmarks"); + + [Benchmark] + public void RemoveItem() => _reactiveList.Remove("ReactiveUI"); + } +} diff --git a/benchmarks/ReactiveUI.Benchmarks/ReactiveUI.Benchmarks.csproj b/benchmarks/ReactiveUI.Benchmarks/ReactiveUI.Benchmarks.csproj new file mode 100644 index 0000000000..1a57033af0 --- /dev/null +++ b/benchmarks/ReactiveUI.Benchmarks/ReactiveUI.Benchmarks.csproj @@ -0,0 +1,19 @@ + + + + netcoreapp2.0 + false + + + + + + + + + + + + + + \ No newline at end of file diff --git a/benchmarks/ReactiveUI.Benchmarks/RoutableViewModelMixinsBenchmarks.cs b/benchmarks/ReactiveUI.Benchmarks/RoutableViewModelMixinsBenchmarks.cs new file mode 100644 index 0000000000..ec4f0336d0 --- /dev/null +++ b/benchmarks/ReactiveUI.Benchmarks/RoutableViewModelMixinsBenchmarks.cs @@ -0,0 +1,54 @@ +using System; +using System.Reactive.Concurrency; +using System.Reactive.Linq; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Attributes.Exporters; +using BenchmarkDotNet.Attributes.Jobs; +using ReactiveUI; + +namespace ReactiveUI.Benchmarks +{ + [CoreJob] + [MarkdownExporterAttribute.GitHub] + public class RoutableViewModelMixinsBenchmarks + { + private Func _mockViewModel; + private RoutingState _router; + + [GlobalSetup] + public void Setup() + { + _router = new RoutingState(); + _mockViewModel = () => new MockViewModel(); + } + + [GlobalCleanup] + public void Cleanup() + { + _router = null; + _mockViewModel = null; + } + + [Benchmark] + public void WhenNavigatedToObservable() + { + _mockViewModel() + .WhenNavigatedToObservable() + .Subscribe(x => + { + Console.WriteLine("Observed"); + }); + + _router.Navigate + .Execute(_mockViewModel()).Subscribe(); + } + + [Benchmark] + public void WhenNavigatingFromObservable() + { + _router.Navigate.Execute(_mockViewModel()).Subscribe(); + _mockViewModel().WhenNavigatingFromObservable().Subscribe(); + _router.NavigateBack.Execute().Subscribe(); + } + } +}