瀏覽代碼

Refactored style attach benchmark.

Now tries to simulate an application with a lot of styles applied at different points in the logical tree.

Make `StyledElement.ApplyStyling` a public API in order to do this.
Steven Kirk 2 年之前
父節點
當前提交
0db8d5a2d2

+ 5 - 1
src/Avalonia.Base/StyledElement.cs

@@ -344,10 +344,14 @@ namespace Avalonia
         /// Applies styling to the control if the control is initialized and styling is not
         /// already applied.
         /// </summary>
+        /// <remarks>
+        /// The styling system will automatically apply styling when required, so it should not
+        /// usually be necessary to call this method manually.
+        /// </remarks>
         /// <returns>
         /// A value indicating whether styling is now applied to the control.
         /// </returns>
-        protected bool ApplyStyling()
+        public bool ApplyStyling()
         {
             if (_initCount == 0 && !_styled)
             {

+ 0 - 47
tests/Avalonia.Benchmarks/Styling/StyleAttachBenchmark.cs

@@ -1,47 +0,0 @@
-using System;
-using System.Runtime.CompilerServices;
-using Avalonia.Controls;
-using Avalonia.Styling;
-using Avalonia.UnitTests;
-using BenchmarkDotNet.Attributes;
-
-namespace Avalonia.Benchmarks.Styling
-{
-    [MemoryDiagnoser]
-    public class StyleAttachBenchmark : IDisposable
-    {
-        private readonly IDisposable _app;
-        private readonly TestRoot _root;
-        private readonly TextBox _control;
-
-        public StyleAttachBenchmark()
-        {
-            _app = UnitTestApplication.Start(
-                TestServices.StyledWindow.With(
-                    renderInterface: new NullRenderingPlatform(),
-                    threadingInterface: new NullThreadingPlatform()));
-
-            _root = new TestRoot(true, null)
-            {
-                Renderer = new NullRenderer(),
-            };
-
-            _control = new TextBox();
-        }
-
-        [Benchmark]
-        [MethodImpl(MethodImplOptions.NoInlining)]
-        public void AttachTextBoxStyles()
-        {
-            var styles = UnitTestApplication.Current.Styles;
-
-            styles.TryAttach(_control, UnitTestApplication.Current);
-            ((IStyleable)_control).DetachStyles();
-        }
-
-        public void Dispose()
-        {
-            _app.Dispose();
-        }
-    }
-}

+ 126 - 0
tests/Avalonia.Benchmarks/Styling/Style_Apply_Detach_Complex.cs

@@ -0,0 +1,126 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+using Avalonia.Controls;
+using Avalonia.Styling;
+using Avalonia.UnitTests;
+using BenchmarkDotNet.Attributes;
+
+namespace Avalonia.Benchmarks.Styling
+{
+    [MemoryDiagnoser]
+    public class Style_Apply_Detach_Complex : IDisposable
+    {
+        private readonly IDisposable _app;
+        private readonly TestRoot _root;
+        private readonly TextBox _control;
+
+        public Style_Apply_Detach_Complex()
+        {
+            _app = UnitTestApplication.Start(
+                TestServices.StyledWindow.With(
+                    renderInterface: new NullRenderingPlatform(),
+                    threadingInterface: new NullThreadingPlatform()));
+
+            // Simulate an application with a lot of styles by creating a tree of nested panels,
+            // each with a bunch of styles applied.
+            var (rootPanel, leafPanel) = CreateNestedPanels(10);
+
+            // We're benchmarking how long it takes to apply styles to a TextBox in this situation.
+            _control = new TextBox();
+            leafPanel.Children.Add(_control);
+
+            _root = new TestRoot(true, rootPanel)
+            {
+                Renderer = new NullRenderer(),
+            };
+        }
+
+        [Benchmark]
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        public void Apply_Detach_Styles()
+        {
+            // Styles will have already been attached when attached to the logical tree, so remove
+            // the styles first.
+            if ((string)_control.Tag != "TextBox")
+                throw new Exception("Invalid benchmark state");
+
+            ((IStyleable)_control).DetachStyles();
+
+            if (_control.Tag is not null)
+                throw new Exception("Invalid benchmark state");
+
+            // Then re-apply the styles.
+            _control.ApplyStyling();
+        }
+
+        public void Dispose()
+        {
+            _app.Dispose();
+        }
+
+        private static (Panel, Panel) CreateNestedPanels(int count)
+        {
+            var root = new Panel();
+            var last = root;
+
+            for (var i = 0; i < count; ++i)
+            {
+                var panel = new Panel();
+                panel.Styles.AddRange(CreateStyles());
+                last.Children.Add(panel);
+                last = panel;
+            }
+
+            return (root, last);
+        }
+
+        private static IEnumerable<IStyle> CreateStyles()
+        {
+            var types = new[]
+            {
+                typeof(Border),
+                typeof(Button),
+                typeof(ButtonSpinner),
+                typeof(Carousel),
+                typeof(CheckBox),
+                typeof(ComboBox),
+                typeof(ContentControl),
+                typeof(Expander),
+                typeof(ItemsControl),
+                typeof(Label),
+                typeof(ListBox),
+                typeof(ProgressBar),
+                typeof(RadioButton),
+                typeof(RepeatButton),
+                typeof(ScrollViewer),
+                typeof(Slider),
+                typeof(Spinner),
+                typeof(SplitView),
+                typeof(TextBox),
+                typeof(ToggleSwitch),
+                typeof(TreeView),
+                typeof(Viewbox),
+                typeof(Window),
+            };
+
+            foreach (var type in types)
+            {
+                yield return new Style(x => x.OfType(type))
+                {
+                    Setters = { new Setter(Control.TagProperty, type.Name) }
+                };
+
+                yield return new Style(x => x.OfType(type).Class("foo"))
+                {
+                    Setters = { new Setter(Control.TagProperty, type.Name + " foo") }
+                };
+
+                yield return new Style(x => x.OfType(type).Class("bar"))
+                {
+                    Setters = { new Setter(Control.TagProperty, type.Name + " bar") }
+                };
+            }
+        }
+    }
+}