소스 검색

Merge pull request #10560 from MarchingCube/selector-perf

Or Selector benchmark and removal of repeated virtual calls
Max Katz 2 년 전
부모
커밋
10fe006553

+ 1 - 1
src/Avalonia.Base/Styling/DescendentSelector.cs

@@ -13,7 +13,7 @@ namespace Avalonia.Styling
 
         public DescendantSelector(Selector? parent)
         {
-            _parent = parent ?? throw new InvalidOperationException("Descendant selector must be preceeded by a selector.");
+            _parent = parent ?? throw new InvalidOperationException("Descendant selector must be preceded by a selector.");
         }
 
         /// <inheritdoc/>

+ 11 - 16
src/Avalonia.Base/Styling/OrSelector.cs

@@ -10,7 +10,7 @@ namespace Avalonia.Styling
     /// <summary>
     /// The OR style selector.
     /// </summary>
-    internal class OrSelector : Selector
+    internal sealed class OrSelector : Selector
     {
         private readonly IReadOnlyList<Selector> _selectors;
         private string? _selectorString;
@@ -42,18 +42,7 @@ namespace Avalonia.Styling
         public override bool IsCombinator => false;
 
         /// <inheritdoc/>
-        public override Type? TargetType
-        {
-            get
-            {
-                if (_targetType == null)
-                {
-                    _targetType = EvaluateTargetType();
-                }
-
-                return _targetType;
-            }
-        }
+        public override Type? TargetType => _targetType ??= EvaluateTargetType();
 
         /// <inheritdoc/>
         public override string ToString(Style? owner)
@@ -71,7 +60,9 @@ namespace Avalonia.Styling
             var activators = new OrActivatorBuilder();
             var neverThisInstance = false;
 
-            for (var i = 0; i < _selectors.Count; i++)
+            var count = _selectors.Count;
+
+            for (var i = 0; i < count; i++)
             {
                 var match = _selectors[i].Match(control, parent, subscribe);
 
@@ -108,7 +99,9 @@ namespace Avalonia.Styling
 
         internal override void ValidateNestingSelector(bool inControlTheme)
         {
-            for (var i = 0; i < _selectors.Count; i++)
+            var count = _selectors.Count;
+
+            for (var i = 0; i < count; i++)
             {
                 _selectors[i].ValidateNestingSelector(inControlTheme);
             }
@@ -118,7 +111,9 @@ namespace Avalonia.Styling
         {
             Type? result = null;
 
-            for (var i = 0; i < _selectors.Count; i++)
+            var count = _selectors.Count;
+
+            for (var i = 0; i < count; i++)
             {
                 var selector = _selectors[i];
                 if (selector.TargetType == null)

+ 2 - 7
src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs

@@ -11,7 +11,7 @@ namespace Avalonia.Styling
     /// A selector that matches the common case of a type and/or name followed by a collection of
     /// style classes and pseudoclasses.
     /// </summary>
-    internal class TypeNameAndClassSelector : Selector
+    internal sealed class TypeNameAndClassSelector : Selector
     {
         private readonly Selector? _previous;
         private List<string>? _classes;
@@ -85,12 +85,7 @@ namespace Avalonia.Styling
         /// <inheritdoc/>
         public override string ToString(Style? owner)
         {
-            if (_selectorString == null)
-            {
-                _selectorString = BuildSelectorString(owner);
-            }
-
-            return _selectorString;
+            return _selectorString ??= BuildSelectorString(owner);
         }
 
         /// <inheritdoc/>

+ 15 - 0
tests/Avalonia.Benchmarks/Properties/launchSettings.json

@@ -0,0 +1,15 @@
+{
+  "profiles": {
+    "Avalonia.Benchmarks": {
+      "commandName": "Project"
+    },
+    "Avalonia.Benchmarks (in-process)": {
+      "commandName": "Project",
+      "commandLineArgs": "--inprocess"
+    },
+    "Avalonia.Benchmarks (debug)": {
+      "commandName": "Project",
+      "commandLineArgs": "--debug"
+    }
+  }
+}

+ 47 - 1
tests/Avalonia.Benchmarks/Styling/SelectorBenchmark.cs

@@ -1,4 +1,5 @@
-using Avalonia.Controls;
+using System;
+using Avalonia.Controls;
 using Avalonia.Styling;
 using BenchmarkDotNet.Attributes;
 
@@ -11,6 +12,8 @@ namespace Avalonia.Benchmarks.Styling
         private readonly Calendar _matchingControl;
         private readonly Selector _isCalendarSelector;
         private readonly Selector _classSelector;
+        private readonly Selector _orSelectorTwo;
+        private readonly Selector _orSelectorFive;
 
         public SelectorBenchmark()
         {
@@ -23,6 +26,14 @@ namespace Avalonia.Benchmarks.Styling
 
             _isCalendarSelector = Selectors.Is<Calendar>(null);
             _classSelector = Selectors.Class(null, className);
+
+            _orSelectorTwo = Selectors.Or(new AlwaysMatchSelector(), new AlwaysMatchSelector());
+            _orSelectorFive = Selectors.Or(
+                new AlwaysMatchSelector(), 
+                new AlwaysMatchSelector(),
+                new AlwaysMatchSelector(),
+                new AlwaysMatchSelector(),
+                new AlwaysMatchSelector());
         }
 
         [Benchmark]
@@ -48,5 +59,40 @@ namespace Avalonia.Benchmarks.Styling
         {
             return _classSelector.Match(_matchingControl);
         }
+
+        [Benchmark]
+        public SelectorMatch OrSelector_One_Match()
+        {
+            return _orSelectorTwo.Match(_matchingControl);
+        }
+
+        [Benchmark]
+        public SelectorMatch OrSelector_Five_Match()
+        {
+            return _orSelectorFive.Match(_matchingControl);
+        }
+    }
+
+    internal class AlwaysMatchSelector : Selector
+    {
+        public override bool InTemplate => false;
+
+        public override bool IsCombinator => false;
+
+        public override Type TargetType => null;
+
+        public override string ToString(Style owner)
+        {
+            return "Always";
+        }
+
+        protected override SelectorMatch Evaluate(StyledElement control, IStyle parent, bool subscribe)
+        {
+            return SelectorMatch.AlwaysThisType;
+        }
+
+        protected override Selector MovePrevious() => null;
+
+        protected override Selector MovePreviousOrParent() => null;
     }
 }