From dca96b5ad3a7f9acca903ca6f9a0191ba5271068 Mon Sep 17 00:00:00 2001 From: Julien Lebosquain Date: Sat, 25 Feb 2023 13:55:38 +0100 Subject: [PATCH 1/4] Avoid boxing iterators in hot paths --- .../Composition/CompositingRenderer.cs | 2 +- src/Avalonia.Base/StyledElement.cs | 21 +++++++++++++------ src/Avalonia.Base/Styling/OrSelector.cs | 13 +++++++----- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs b/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs index 01299e4ffa..df3a70b3e6 100644 --- a/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs +++ b/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs @@ -225,7 +225,7 @@ public class CompositingRenderer : IRendererWithCompositor sortedChildren.Dispose(); } else - foreach (var ch in v.GetVisualChildren()) + foreach (var ch in visualChildren) { var compositionChild = ch.CompositionVisual; if (compositionChild != null) diff --git a/src/Avalonia.Base/StyledElement.cs b/src/Avalonia.Base/StyledElement.cs index 82e948eea8..cbdf3c3c1e 100644 --- a/src/Avalonia.Base/StyledElement.cs +++ b/src/Avalonia.Base/StyledElement.cs @@ -803,8 +803,11 @@ namespace Avalonia if (theme.HasChildren) { - foreach (var child in theme.Children) - ApplyStyle(child, null, type); + var children = theme.Children; + for (var i = 0; i < children.Count; i++) + { + ApplyStyle(children[i], null, type); + } } } @@ -816,8 +819,11 @@ namespace Avalonia if (host.IsStylesInitialized) { - foreach (var style in host.Styles) - ApplyStyle(style, host, FrameType.Style); + var styles = host.Styles; + for (var i = 0; i < styles.Count; ++i) + { + ApplyStyle(styles[i], host, FrameType.Style); + } } } @@ -826,8 +832,11 @@ namespace Avalonia if (style is Style s) s.TryAttach(this, host, type); - foreach (var child in style.Children) - ApplyStyle(child, host, type); + var children = style.Children; + for (var i = 0; i < children.Count; i++) + { + ApplyStyle(children[i], host, type); + } } private void ReevaluateImplicitTheme() diff --git a/src/Avalonia.Base/Styling/OrSelector.cs b/src/Avalonia.Base/Styling/OrSelector.cs index c5ef9a0b2b..3b0aa03492 100644 --- a/src/Avalonia.Base/Styling/OrSelector.cs +++ b/src/Avalonia.Base/Styling/OrSelector.cs @@ -71,9 +71,9 @@ namespace Avalonia.Styling var activators = new OrActivatorBuilder(); var neverThisInstance = false; - foreach (var selector in _selectors) + for (var i = 0; i < _selectors.Count; i++) { - var match = selector.Match(control, parent, subscribe); + var match = _selectors[i].Match(control, parent, subscribe); switch (match.Result) { @@ -108,16 +108,19 @@ namespace Avalonia.Styling internal override void ValidateNestingSelector(bool inControlTheme) { - foreach (var selector in _selectors) - selector.ValidateNestingSelector(inControlTheme); + for (var i = 0; i < _selectors.Count; i++) + { + _selectors[i].ValidateNestingSelector(inControlTheme); + } } private Type? EvaluateTargetType() { Type? result = null; - foreach (var selector in _selectors) + for (var i = 0; i < _selectors.Count; i++) { + var selector = _selectors[i]; if (selector.TargetType == null) { return null; From 765bce78d78bb4aa90d2ce264a18e7b066e3a32f Mon Sep 17 00:00:00 2001 From: Julien Lebosquain Date: Sat, 25 Feb 2023 14:25:02 +0100 Subject: [PATCH 2/4] Avoid unneeded allocations in CompiledBindingPath --- .../CompiledBindings/CompiledBindingPath.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs index 2b62d33349..f196231a6b 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.Linq; using System.Reflection; using Avalonia.Controls; using Avalonia.Data.Core; @@ -13,13 +12,14 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings { public class CompiledBindingPath { - private readonly List _elements = new List(); + private readonly ICompiledBindingPathElement[] _elements; - public CompiledBindingPath() { } + public CompiledBindingPath() + => _elements = Array.Empty(); - internal CompiledBindingPath(IEnumerable bindingPath, object rawSource) + internal CompiledBindingPath(ICompiledBindingPathElement[] elements, object rawSource) { - _elements = new List(bindingPath); + _elements = elements; RawSource = rawSource; } @@ -78,13 +78,13 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings internal IEnumerable Elements => _elements; - internal SourceMode SourceMode => _elements.OfType().Any() + internal SourceMode SourceMode => Array.Exists(_elements, e => e is IControlSourceBindingPathElement) ? SourceMode.Control : SourceMode.Data; internal object RawSource { get; } public override string ToString() - => string.Concat(_elements); + => string.Concat((IEnumerable) _elements); } public class CompiledBindingPathBuilder @@ -169,7 +169,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings return this; } - public CompiledBindingPath Build() => new CompiledBindingPath(_elements, _rawSource); + public CompiledBindingPath Build() => new CompiledBindingPath(_elements.ToArray(), _rawSource); } public interface ICompiledBindingPathElement From 79af58b32f93312c5ba519e98f1192a8bd432796 Mon Sep 17 00:00:00 2001 From: Julien Lebosquain Date: Sat, 25 Feb 2023 15:06:30 +0100 Subject: [PATCH 3/4] Adjusted AvaloniaPropertyDictionary growth rate to reduce allocations --- .../Utilities/AvaloniaPropertyDictionary.cs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.Base/Utilities/AvaloniaPropertyDictionary.cs b/src/Avalonia.Base/Utilities/AvaloniaPropertyDictionary.cs index 5cac2ef658..0589abb2dd 100644 --- a/src/Avalonia.Base/Utilities/AvaloniaPropertyDictionary.cs +++ b/src/Avalonia.Base/Utilities/AvaloniaPropertyDictionary.cs @@ -304,13 +304,9 @@ namespace Avalonia.Utilities { if (_entryCount == _entries!.Length) { - const double growthFactor = 1.2; - var newSize = (int)(_entryCount * growthFactor); - - if (newSize == _entryCount) - { - newSize++; - } + var newSize = _entryCount == DefaultInitialCapacity ? + DefaultInitialCapacity * 2 : + (int)(_entryCount * 1.5); var destEntries = new Entry[newSize]; From 45ba9c98dff7791f46aaea2cc3ba0b863707cbd8 Mon Sep 17 00:00:00 2001 From: Julien Lebosquain Date: Sat, 25 Feb 2023 16:21:55 +0100 Subject: [PATCH 4/4] Reduce allocations in TypeNameAndClassSelector.Classes --- .../Styling/TypeNameAndClassSelector.cs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs b/src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs index 7883fd23ab..f8670cfdb3 100644 --- a/src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs +++ b/src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using Avalonia.Controls; using Avalonia.Styling.Activators; using Avalonia.Utilities; @@ -15,7 +14,7 @@ namespace Avalonia.Styling internal class TypeNameAndClassSelector : Selector { private readonly Selector? _previous; - private readonly Lazy> _classes = new Lazy>(() => new List()); + private List? _classes; private Type? _targetType; private string? _selectorString; @@ -81,7 +80,7 @@ namespace Avalonia.Styling /// /// The style classes which the selector matches. /// - public IList Classes => _classes.Value; + public IList Classes => _classes ??= new(); /// public override string ToString(Style? owner) @@ -122,16 +121,16 @@ namespace Avalonia.Styling return SelectorMatch.NeverThisInstance; } - if (_classes.IsValueCreated && _classes.Value.Count > 0) + if (_classes is { Count: > 0 }) { if (subscribe) { - var observable = new StyleClassActivator((Classes)control.Classes, _classes.Value); + var observable = new StyleClassActivator(control.Classes, _classes); return new SelectorMatch(observable); } - if (!StyleClassActivator.AreClassesMatching(control.Classes, Classes)) + if (!StyleClassActivator.AreClassesMatching(control.Classes, _classes)) { return SelectorMatch.NeverThisInstance; } @@ -172,9 +171,9 @@ namespace Avalonia.Styling builder.Append(Name); } - if (_classes.IsValueCreated && _classes.Value.Count > 0) + if (_classes is { Count: > 0 }) { - foreach (var c in Classes) + foreach (var c in _classes) { if (!c.StartsWith(":")) {