From db85d052a0f27f032e17064bacbe98dec8752109 Mon Sep 17 00:00:00 2001 From: Peter Vietense Date: Sat, 9 Jul 2022 08:44:16 +0200 Subject: [PATCH] Performance Improvement IBusyStack implementations --- .editorconfig | 34 +++++++++++++++- MvvmScarletToolkit.sln | 3 +- .../IBusyStack.cs | 5 +-- .../ViewModels/Base/ViewModelBase.cs | 2 +- .../ViewModels/State/BusyStack.cs | 31 +++++++-------- .../ViewModels/State/BusyToken.cs | 2 +- .../ViewModels/State/ObservableBusyStack.cs | 39 +++++++------------ .../Features/Busy/BusyViewModel.cs | 8 ++-- version.json | 2 +- 9 files changed, 68 insertions(+), 58 deletions(-) diff --git a/.editorconfig b/.editorconfig index b96fe015..7f74512d 100644 --- a/.editorconfig +++ b/.editorconfig @@ -6,6 +6,25 @@ indent_size = 4 charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true +dotnet_style_coalesce_expression = true:silent +dotnet_style_null_propagation = true:silent +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_object_initializer = true:warning +dotnet_style_collection_initializer = true:warning +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = false:silent +dotnet_style_prefer_conditional_expression_over_return = false:suggestion +dotnet_style_explicit_tuple_names = true:error +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = false:suggestion +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion +dotnet_style_namespace_match_folder = true:suggestion +dotnet_style_readonly_field = true:warning +dotnet_style_operator_placement_when_wrapping = beginning_of_line +tab_width = 4 +end_of_line = crlf [*.cs] @@ -23,7 +42,7 @@ dotnet_naming_symbols.public_symbols.applicable_kinds = property,metho dotnet_naming_symbols.public_symbols.applicable_accessibilities = public dotnet_naming_symbols.public_symbols.required_modifiers = readonly -dotnet_naming_rule.public_members_must_be_capitalized.style = first_word_upper_case_style +dotnet_naming_rule.public_members_must_be_capitalized.style = first_word_upper_case_style dotnet_naming_style.first_word_upper_case_style.capitalization = first_word_upper dotnet_naming_rule.public_members_must_be_capitalized.severity = warning @@ -135,7 +154,7 @@ csharp_prefer_simple_using_statement = false:suggestion dotnet_style_prefer_simplified_boolean_expressions = true:suggestion # IDE0058: Expression value is never used -csharp_style_unused_value_expression_statement_preference = discard_variable +csharp_style_unused_value_expression_statement_preference = discard_variable:silent dotnet_diagnostic.IDE0058.severity = none # IDE0045: Use conditional expression for assignment @@ -150,3 +169,14 @@ dotnet_diagnostic.CA1806.severity = none # IDE0130: Namespace does not match folder structure dotnet_style_namespace_match_folder = true dotnet_diagnostic.IDE0130.severity = none +csharp_style_namespace_declarations = block_scoped:silent +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent +csharp_style_prefer_null_check_over_type_check = true:suggestion +csharp_style_prefer_local_over_anonymous_function = true:suggestion +csharp_style_prefer_index_operator = true:suggestion +csharp_style_prefer_range_operator = true:suggestion +csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion +csharp_style_prefer_tuple_swap = true:suggestion +csharp_style_unused_value_assignment_preference = discard_variable:suggestion diff --git a/MvvmScarletToolkit.sln b/MvvmScarletToolkit.sln index 31ada1fa..c14b244b 100644 --- a/MvvmScarletToolkit.sln +++ b/MvvmScarletToolkit.sln @@ -12,12 +12,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .editorconfig = .editorconfig .gitignore = .gitignore .vsconfig = .vsconfig - appveyor.yml = appveyor.yml - build.cake = build.cake src\Directory.Build.props = src\Directory.Build.props src\Directory.Build.targets = src\Directory.Build.targets MvvmScarletToolkit.ruleset = MvvmScarletToolkit.ruleset README.md = README.md + version.json = version.json EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MvvmScarletToolkit.Incubator", "src\MvvmScarletToolkit.Incubator\MvvmScarletToolkit.Incubator.csproj", "{67E41CB4-774B-455B-B0FC-86EF13677FB7}" diff --git a/src/MvvmScarletToolkit.Abstractions/IBusyStack.cs b/src/MvvmScarletToolkit.Abstractions/IBusyStack.cs index e4b317dd..62fc0a2f 100644 --- a/src/MvvmScarletToolkit.Abstractions/IBusyStack.cs +++ b/src/MvvmScarletToolkit.Abstractions/IBusyStack.cs @@ -1,5 +1,4 @@ using System; -using System.Threading.Tasks; namespace MvvmScarletToolkit { @@ -13,12 +12,12 @@ public interface IBusyStack /// /// remove a token from the stack /// - Task Pull(); + void Pull(); /// /// add a token to the stack /// /// - Task Push(IDisposable token); + void Push(); } } diff --git a/src/MvvmScarletToolkit.Observables/ViewModels/Base/ViewModelBase.cs b/src/MvvmScarletToolkit.Observables/ViewModels/Base/ViewModelBase.cs index 9e81b8b6..4fcc6153 100644 --- a/src/MvvmScarletToolkit.Observables/ViewModels/Base/ViewModelBase.cs +++ b/src/MvvmScarletToolkit.Observables/ViewModels/Base/ViewModelBase.cs @@ -53,7 +53,7 @@ protected virtual void Dispose(bool disposing) if (disposing) { - BusyStack?.Dispose(); + BusyStack.Dispose(); IsActive = false; } diff --git a/src/MvvmScarletToolkit.Observables/ViewModels/State/BusyStack.cs b/src/MvvmScarletToolkit.Observables/ViewModels/State/BusyStack.cs index f76a4aaf..4ddb369d 100644 --- a/src/MvvmScarletToolkit.Observables/ViewModels/State/BusyStack.cs +++ b/src/MvvmScarletToolkit.Observables/ViewModels/State/BusyStack.cs @@ -1,7 +1,6 @@ using System; -using System.Collections.Concurrent; using System.Diagnostics; -using System.Threading.Tasks; +using System.Threading; namespace MvvmScarletToolkit.Observables { @@ -10,45 +9,41 @@ namespace MvvmScarletToolkit.Observables /// public sealed class BusyStack : IBusyStack { - private readonly ConcurrentBag _items; private readonly Action _onChanged; + private int _items; + public BusyStack(in Action onChanged) { _onChanged = onChanged ?? throw new ArgumentNullException(nameof(onChanged)); - _items = new ConcurrentBag(); } - public Task Pull() + public void Pull() { - var oldValue = _items.TryPeek(out _); - _ = _items.TryTake(out _); - var newValue = _items.TryPeek(out _); + var oldValue = _items > 0; + Interlocked.Decrement(ref _items); + var newValue = _items > 0; if (oldValue.Equals(newValue)) { - return Task.CompletedTask; + return; } InvokeOnChanged(newValue); - - return Task.CompletedTask; } - public Task Push(IDisposable token) + public void Push() { - var oldValue = _items.TryPeek(out _); - _items.Add(token); - var newValue = _items.TryPeek(out _); + var oldValue = _items > 0; + Interlocked.Increment(ref _items); + var newValue = _items > 0; if (oldValue.Equals(newValue)) { - return Task.CompletedTask; + return; } InvokeOnChanged(newValue); - - return Task.CompletedTask; } /// diff --git a/src/MvvmScarletToolkit.Observables/ViewModels/State/BusyToken.cs b/src/MvvmScarletToolkit.Observables/ViewModels/State/BusyToken.cs index ca818f81..344a6c90 100644 --- a/src/MvvmScarletToolkit.Observables/ViewModels/State/BusyToken.cs +++ b/src/MvvmScarletToolkit.Observables/ViewModels/State/BusyToken.cs @@ -13,7 +13,7 @@ public BusyToken(in IBusyStack busyStack) _busyStack = busyStack ?? throw new ArgumentNullException(nameof(busyStack)); _disposed = false; - busyStack.Push(this); + busyStack.Push(); } public void Dispose() diff --git a/src/MvvmScarletToolkit.Observables/ViewModels/State/ObservableBusyStack.cs b/src/MvvmScarletToolkit.Observables/ViewModels/State/ObservableBusyStack.cs index 51fdd8fe..d111ad53 100644 --- a/src/MvvmScarletToolkit.Observables/ViewModels/State/ObservableBusyStack.cs +++ b/src/MvvmScarletToolkit.Observables/ViewModels/State/ObservableBusyStack.cs @@ -2,7 +2,7 @@ using System.Collections.Concurrent; using System.Diagnostics; using System.Linq; -using System.Threading.Tasks; +using System.Threading; namespace MvvmScarletToolkit.Observables { @@ -13,10 +13,10 @@ namespace MvvmScarletToolkit.Observables public sealed class ObservableBusyStack : IObservableBusyStack { private readonly string _id; - private readonly ConcurrentBag _items; private readonly ConcurrentDictionary, object> _observers; private readonly Action _onChanged; + private int _items; private bool _disposed; public ObservableBusyStack(in Action onChanged) @@ -24,24 +24,23 @@ public ObservableBusyStack(in Action onChanged) _onChanged = onChanged ?? throw new ArgumentNullException(nameof(onChanged)); _id = Convert.ToBase64String(Guid.NewGuid().ToByteArray()); - _items = new ConcurrentBag(); _observers = new ConcurrentDictionary, object>(); } - public Task Pull() + public void Pull() { if (_disposed) { throw new ObjectDisposedException(nameof(ObservableBusyStack)); } - var oldValue = _items.TryPeek(out _); - _ = _items.TryTake(out _); - var newValue = _items.TryPeek(out _); + var oldValue = _items > 0; + Interlocked.Decrement(ref _items); + var newValue = _items > 0; if (oldValue.Equals(newValue)) { - return Task.CompletedTask; + return; } #if DEBUG @@ -49,32 +48,28 @@ public Task Pull() #endif Notify(newValue); - - return Task.CompletedTask; } - public Task Push(IDisposable token) + public void Push() { if (_disposed) { throw new ObjectDisposedException(nameof(ObservableBusyStack)); } - var oldValue = _items.TryPeek(out _); - _items.Add(token); - var newValue = _items.TryPeek(out _); + var oldValue = _items > 0; + Interlocked.Increment(ref _items); + var newValue = _items > 0; if (oldValue.Equals(newValue)) { - return Task.CompletedTask; + return; } #if DEBUG Debug.WriteLine($"ObservableBusyStack({_id}) PUSH HasItems: {newValue}"); #endif Notify(newValue); - - return Task.CompletedTask; } private void Notify(bool hasItems) @@ -150,21 +145,13 @@ public void Dispose() return; } - for (var i = 0; i < _items.Count; i++) - { - if (_items.TryTake(out var item)) - { - item.Dispose(); - } - } - _observers.Clear(); _disposed = true; } public override string ToString() { - return $"{_id} HasItems: {!_items.IsEmpty}"; + return $"{_id} HasItems: {_items > 0}"; } } } diff --git a/src/MvvmScarletToolkit.Wpf.Samples/Features/Busy/BusyViewModel.cs b/src/MvvmScarletToolkit.Wpf.Samples/Features/Busy/BusyViewModel.cs index 1f349329..94c71953 100644 --- a/src/MvvmScarletToolkit.Wpf.Samples/Features/Busy/BusyViewModel.cs +++ b/src/MvvmScarletToolkit.Wpf.Samples/Features/Busy/BusyViewModel.cs @@ -68,17 +68,17 @@ public void OnNext(bool value) BusyStack.GetToken(); } - public async void OnError(Exception error) + public void OnError(Exception error) { - await BusyStack.Pull().ConfigureAwait(false); + BusyStack.Pull(); } /// /// Unused /// - public async void OnCompleted() + public void OnCompleted() { - await BusyStack.Pull().ConfigureAwait(false); + BusyStack.Pull(); } protected override void Dispose(bool disposing) diff --git a/version.json b/version.json index 56efe520..1a1ccf5d 100644 --- a/version.json +++ b/version.json @@ -1,6 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", - "version": "2.2", + "version": "3.0", "publicReleaseRefSpec": [ "^refs/heads/master$", "^refs/heads/v\\d+\\.\\d+$"