From dc698b3b1d404a92aebdf7183c13fe9769fcc116 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Thu, 19 Nov 2020 12:26:11 +0100 Subject: [PATCH] Reuse empty value store arrays. --- .../Utilities/AvaloniaPropertyValueStore.cs | 24 +++++++++++++++---- .../Layout/ControlsBenchmark.cs | 3 ++- .../Avalonia.Benchmarks/NullCursorFactory.cs | 14 +++++++++++ 3 files changed, 36 insertions(+), 5 deletions(-) create mode 100644 tests/Avalonia.Benchmarks/NullCursorFactory.cs diff --git a/src/Avalonia.Base/Utilities/AvaloniaPropertyValueStore.cs b/src/Avalonia.Base/Utilities/AvaloniaPropertyValueStore.cs index 0238446892..6e52b6770a 100644 --- a/src/Avalonia.Base/Utilities/AvaloniaPropertyValueStore.cs +++ b/src/Avalonia.Base/Utilities/AvaloniaPropertyValueStore.cs @@ -1,5 +1,8 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +#nullable enable namespace Avalonia.Utilities { @@ -9,12 +12,14 @@ namespace Avalonia.Utilities /// Stored value type. internal sealed class AvaloniaPropertyValueStore { + // The last item in the list is always int.MaxValue. + private static readonly Entry[] s_emptyEntries = { new Entry { PropertyId = int.MaxValue, Value = default! } }; + private Entry[] _entries; public AvaloniaPropertyValueStore() { - // The last item in the list is always int.MaxValue - _entries = new[] { new Entry { PropertyId = int.MaxValue, Value = default } }; + _entries = s_emptyEntries; } private (int, bool) TryFindEntry(int propertyId) @@ -86,7 +91,7 @@ namespace Avalonia.Utilities return (0, false); } - public bool TryGetValue(AvaloniaProperty property, out TValue value) + public bool TryGetValue(AvaloniaProperty property, [MaybeNull] out TValue value) { (int index, bool found) = TryFindEntry(property.Id); if (!found) @@ -132,7 +137,18 @@ namespace Avalonia.Utilities if (found) { - Entry[] entries = new Entry[_entries.Length - 1]; + var newLength = _entries.Length - 1; + + // Special case - one element left means that value store is empty so we can just reuse our "empty" array. + if (newLength == 1) + { + _entries = s_emptyEntries; + + return; + } + + var entries = new Entry[newLength]; + int ix = 0; for (int i = 0; i < _entries.Length; ++i) diff --git a/tests/Avalonia.Benchmarks/Layout/ControlsBenchmark.cs b/tests/Avalonia.Benchmarks/Layout/ControlsBenchmark.cs index 7170f6d7d4..3493dd0f53 100644 --- a/tests/Avalonia.Benchmarks/Layout/ControlsBenchmark.cs +++ b/tests/Avalonia.Benchmarks/Layout/ControlsBenchmark.cs @@ -17,7 +17,8 @@ namespace Avalonia.Benchmarks.Layout _app = UnitTestApplication.Start( TestServices.StyledWindow.With( renderInterface: new NullRenderingPlatform(), - threadingInterface: new NullThreadingPlatform())); + threadingInterface: new NullThreadingPlatform(), + standardCursorFactory: new NullCursorFactory())); _root = new TestRoot(true, null) { diff --git a/tests/Avalonia.Benchmarks/NullCursorFactory.cs b/tests/Avalonia.Benchmarks/NullCursorFactory.cs new file mode 100644 index 0000000000..012adce0f2 --- /dev/null +++ b/tests/Avalonia.Benchmarks/NullCursorFactory.cs @@ -0,0 +1,14 @@ +using System; +using Avalonia.Input; +using Avalonia.Platform; + +namespace Avalonia.Benchmarks +{ + internal class NullCursorFactory : IStandardCursorFactory + { + public IPlatformHandle GetCursor(StandardCursorType cursorType) + { + return new PlatformHandle(IntPtr.Zero, "null"); + } + } +}