diff --git a/tests/Avalonia.Benchmarks/Avalonia.Benchmarks.csproj b/tests/Avalonia.Benchmarks/Avalonia.Benchmarks.csproj
index 3994d20c68..2dbb054221 100644
--- a/tests/Avalonia.Benchmarks/Avalonia.Benchmarks.csproj
+++ b/tests/Avalonia.Benchmarks/Avalonia.Benchmarks.csproj
@@ -19,7 +19,7 @@
-
+
diff --git a/tests/Avalonia.Benchmarks/Base/AvaloniaObject_Binding.cs b/tests/Avalonia.Benchmarks/Base/AvaloniaObject_Binding.cs
new file mode 100644
index 0000000000..885e202544
--- /dev/null
+++ b/tests/Avalonia.Benchmarks/Base/AvaloniaObject_Binding.cs
@@ -0,0 +1,149 @@
+using System.Runtime.CompilerServices;
+using Avalonia.Data;
+using BenchmarkDotNet.Attributes;
+
+#nullable enable
+
+namespace Avalonia.Benchmarks.Base
+{
+ [MemoryDiagnoser]
+ public class AvaloniaObject_Binding
+ {
+ private static TestClass _target = null!;
+ private static TestBindingObservable s_stringSource = new();
+ private static TestBindingObservable s_struct1Source = new();
+ private static TestBindingObservable s_struct2Source = new();
+ private static TestBindingObservable s_struct3Source = new();
+ private static TestBindingObservable s_struct4Source = new();
+ private static TestBindingObservable s_struct5Source = new();
+ private static TestBindingObservable s_struct6Source = new();
+ private static TestBindingObservable s_struct7Source = new();
+ private static TestBindingObservable s_struct8Source = new();
+
+ public AvaloniaObject_Binding()
+ {
+ RuntimeHelpers.RunClassConstructor(typeof(TestClass).TypeHandle);
+ }
+
+ [GlobalSetup]
+ public void Setup()
+ {
+ _target = new TestClass();
+ }
+
+ [Benchmark]
+ public void Setup_Dispose_LocalValue_Bindings()
+ {
+ var target = _target;
+
+ for (var i = 0; i < 100; ++i)
+ {
+ using var s0 = target.Bind(TestClass.StringProperty, s_stringSource);
+ using var s1 = target.Bind(TestClass.Struct1Property, s_struct1Source);
+ using var s2 = target.Bind(TestClass.Struct2Property, s_struct2Source);
+ using var s3 = target.Bind(TestClass.Struct3Property, s_struct3Source);
+ using var s4 = target.Bind(TestClass.Struct4Property, s_struct4Source);
+ using var s5 = target.Bind(TestClass.Struct5Property, s_struct5Source);
+ using var s6 = target.Bind(TestClass.Struct6Property, s_struct6Source);
+ using var s7 = target.Bind(TestClass.Struct7Property, s_struct7Source);
+ using var s8 = target.Bind(TestClass.Struct8Property, s_struct8Source);
+ }
+ }
+
+
+ [Benchmark]
+ public void Fire_LocalValue_Bindings()
+ {
+ var target = _target;
+
+ using var s0 = target.Bind(TestClass.StringProperty, s_stringSource);
+ using var s1 = target.Bind(TestClass.Struct1Property, s_struct1Source);
+ using var s2 = target.Bind(TestClass.Struct2Property, s_struct2Source);
+ using var s3 = target.Bind(TestClass.Struct3Property, s_struct3Source);
+ using var s4 = target.Bind(TestClass.Struct4Property, s_struct4Source);
+ using var s5 = target.Bind(TestClass.Struct5Property, s_struct5Source);
+ using var s6 = target.Bind(TestClass.Struct6Property, s_struct6Source);
+ using var s7 = target.Bind(TestClass.Struct7Property, s_struct7Source);
+ using var s8 = target.Bind(TestClass.Struct8Property, s_struct8Source);
+
+ for (var i = 0; i < 100; ++i)
+ {
+ s_stringSource.OnNext(i.ToString());
+ s_struct1Source.OnNext(new(i + 1));
+ s_struct2Source.OnNext(new(i + 1));
+ s_struct3Source.OnNext(new(i + 1));
+ s_struct4Source.OnNext(new(i + 1));
+ s_struct5Source.OnNext(new(i + 1));
+ s_struct6Source.OnNext(new(i + 1));
+ s_struct7Source.OnNext(new(i + 1));
+ s_struct8Source.OnNext(new(i + 1));
+ }
+ }
+
+ [GlobalSetup(Target = nameof(Fire_LocalValue_Bindings_With_Style_Values))]
+ public void SetupStyleValues()
+ {
+ _target = new TestClass();
+ _target.SetValue(TestClass.StringProperty, "foo", BindingPriority.Style);
+ _target.SetValue(TestClass.Struct1Property, new(), BindingPriority.Style);
+ _target.SetValue(TestClass.Struct2Property, new(), BindingPriority.Style);
+ _target.SetValue(TestClass.Struct3Property, new(), BindingPriority.Style);
+ _target.SetValue(TestClass.Struct4Property, new(), BindingPriority.Style);
+ _target.SetValue(TestClass.Struct5Property, new(), BindingPriority.Style);
+ _target.SetValue(TestClass.Struct6Property, new(), BindingPriority.Style);
+ _target.SetValue(TestClass.Struct7Property, new(), BindingPriority.Style);
+ _target.SetValue(TestClass.Struct8Property, new(), BindingPriority.Style);
+ }
+
+ [Benchmark]
+ public void Fire_LocalValue_Bindings_With_Style_Values()
+ {
+ var target = _target;
+
+ using var s0 = target.Bind(TestClass.StringProperty, s_stringSource);
+ using var s1 = target.Bind(TestClass.Struct1Property, s_struct1Source);
+ using var s2 = target.Bind(TestClass.Struct2Property, s_struct2Source);
+ using var s3 = target.Bind(TestClass.Struct3Property, s_struct3Source);
+ using var s4 = target.Bind(TestClass.Struct4Property, s_struct4Source);
+ using var s5 = target.Bind(TestClass.Struct5Property, s_struct5Source);
+ using var s6 = target.Bind(TestClass.Struct6Property, s_struct6Source);
+ using var s7 = target.Bind(TestClass.Struct7Property, s_struct7Source);
+ using var s8 = target.Bind(TestClass.Struct8Property, s_struct8Source);
+
+ for (var i = 0; i < 100; ++i)
+ {
+ s_stringSource.OnNext(i.ToString());
+ s_struct1Source.OnNext(new(i + 1));
+ s_struct2Source.OnNext(new(i + 1));
+ s_struct3Source.OnNext(new(i + 1));
+ s_struct4Source.OnNext(new(i + 1));
+ s_struct5Source.OnNext(new(i + 1));
+ s_struct6Source.OnNext(new(i + 1));
+ s_struct7Source.OnNext(new(i + 1));
+ s_struct8Source.OnNext(new(i + 1));
+ }
+ }
+
+ private class TestClass : AvaloniaObject
+ {
+ public static readonly StyledProperty StringProperty =
+ AvaloniaProperty.Register("String");
+ public static readonly StyledProperty Struct1Property =
+ AvaloniaProperty.Register("Struct1");
+ public static readonly StyledProperty Struct2Property =
+ AvaloniaProperty.Register("Struct2");
+ public static readonly StyledProperty Struct3Property =
+ AvaloniaProperty.Register("Struct3");
+ public static readonly StyledProperty Struct4Property =
+ AvaloniaProperty.Register("Struct4");
+ public static readonly StyledProperty Struct5Property =
+ AvaloniaProperty.Register("Struct5");
+ public static readonly StyledProperty Struct6Property =
+ AvaloniaProperty.Register("Struct6");
+ public static readonly StyledProperty Struct7Property =
+ AvaloniaProperty.Register("Struct7");
+ public static readonly StyledProperty Struct8Property =
+ AvaloniaProperty.Register("Struct8");
+ }
+ }
+}
diff --git a/tests/Avalonia.Benchmarks/Base/AvaloniaObject_Construct.cs b/tests/Avalonia.Benchmarks/Base/AvaloniaObject_Construct.cs
new file mode 100644
index 0000000000..1f07d4e867
--- /dev/null
+++ b/tests/Avalonia.Benchmarks/Base/AvaloniaObject_Construct.cs
@@ -0,0 +1,81 @@
+using System.Runtime.CompilerServices;
+using BenchmarkDotNet.Attributes;
+
+#nullable enable
+
+namespace Avalonia.Benchmarks.Base
+{
+ [MemoryDiagnoser]
+ public class AvaloniaObject_Construct
+ {
+ public AvaloniaObject_Construct()
+ {
+ RuntimeHelpers.RunClassConstructor(typeof(TestClass).TypeHandle);
+ }
+
+ [Benchmark(Baseline = true)]
+ public void ConstructClrObject_And_Set_Values()
+ {
+ var target = new BaselineTestClass();
+ target.StringProperty = "foo";
+ target.Struct1Property = new(1);
+ target.Struct2Property = new(1);
+ target.Struct3Property = new(1);
+ target.Struct4Property = new(1);
+ target.Struct5Property = new(1);
+ target.Struct6Property = new(1);
+ target.Struct7Property = new(1);
+ target.Struct8Property = new(1);
+ }
+
+ [Benchmark]
+ public void Construct_And_Set_Values()
+ {
+ var target = new TestClass();
+ target.SetValue(TestClass.StringProperty, "foo");
+ target.SetValue(TestClass.Struct1Property, new(1));
+ target.SetValue(TestClass.Struct2Property, new(1));
+ target.SetValue(TestClass.Struct3Property, new(1));
+ target.SetValue(TestClass.Struct4Property, new(1));
+ target.SetValue(TestClass.Struct5Property, new(1));
+ target.SetValue(TestClass.Struct6Property, new(1));
+ target.SetValue(TestClass.Struct7Property, new(1));
+ target.SetValue(TestClass.Struct8Property, new(1));
+ }
+
+ private class TestClass : AvaloniaObject
+ {
+ public static readonly StyledProperty StringProperty =
+ AvaloniaProperty.Register("String");
+ public static readonly StyledProperty Struct1Property =
+ AvaloniaProperty.Register("Struct1");
+ public static readonly StyledProperty Struct2Property =
+ AvaloniaProperty.Register("Struct2");
+ public static readonly StyledProperty Struct3Property =
+ AvaloniaProperty.Register("Struct3");
+ public static readonly StyledProperty Struct4Property =
+ AvaloniaProperty.Register("Struct4");
+ public static readonly StyledProperty Struct5Property =
+ AvaloniaProperty.Register("Struct5");
+ public static readonly StyledProperty Struct6Property =
+ AvaloniaProperty.Register("Struct6");
+ public static readonly StyledProperty Struct7Property =
+ AvaloniaProperty.Register("Struct7");
+ public static readonly StyledProperty Struct8Property =
+ AvaloniaProperty.Register("Struct8");
+ }
+
+ private class BaselineTestClass
+ {
+ public string? StringProperty { get; set; }
+ public Struct1 Struct1Property { get; set; }
+ public Struct2 Struct2Property { get; set; }
+ public Struct3 Struct3Property { get; set; }
+ public Struct4 Struct4Property { get; set; }
+ public Struct5 Struct5Property { get; set; }
+ public Struct6 Struct6Property { get; set; }
+ public Struct7 Struct7Property { get; set; }
+ public Struct8 Struct8Property { get; set; }
+ }
+ }
+}
diff --git a/tests/Avalonia.Benchmarks/Base/AvaloniaObject_GetObservable.cs b/tests/Avalonia.Benchmarks/Base/AvaloniaObject_GetObservable.cs
new file mode 100644
index 0000000000..9157dd156c
--- /dev/null
+++ b/tests/Avalonia.Benchmarks/Base/AvaloniaObject_GetObservable.cs
@@ -0,0 +1,171 @@
+using System;
+using System.Runtime.CompilerServices;
+using BenchmarkDotNet.Attributes;
+
+#nullable enable
+
+namespace Avalonia.Benchmarks.Base
+{
+ [MemoryDiagnoser]
+ public class AvaloniaObject_GetObservable
+ {
+ private TestClass _target = null!;
+ public static int result;
+
+ public AvaloniaObject_GetObservable()
+ {
+ RuntimeHelpers.RunClassConstructor(typeof(TestClass).TypeHandle);
+ }
+
+ [GlobalSetup]
+ public void Setup()
+ {
+ _target = new();
+ }
+
+ [Benchmark(Baseline = true)]
+ public void PropertyChangedSubscription()
+ {
+ var target = _target;
+
+ static void ChangeHandler(object? sender, AvaloniaPropertyChangedEventArgs e)
+ {
+ if (e.Property == TestClass.StringProperty)
+ {
+ var ev = (AvaloniaPropertyChangedEventArgs)e;
+ result += ev.NewValue.Value?.Length ?? 0;
+ }
+ else if (e.Property == TestClass.Struct1Property)
+ {
+ var ev = (AvaloniaPropertyChangedEventArgs)e;
+ result += ev.NewValue.Value.Int1;
+ }
+ else if (e.Property == TestClass.Struct2Property)
+ {
+ var ev = (AvaloniaPropertyChangedEventArgs)e;
+ result += ev.NewValue.Value.Int1;
+ }
+ else if (e.Property == TestClass.Struct3Property)
+ {
+ var ev = (AvaloniaPropertyChangedEventArgs)e;
+ result += ev.NewValue.Value.Int1;
+ }
+ else if (e.Property == TestClass.Struct4Property)
+ {
+ var ev = (AvaloniaPropertyChangedEventArgs)e;
+ result += ev.NewValue.Value.Int1;
+ }
+ else if (e.Property == TestClass.Struct5Property)
+ {
+ var ev = (AvaloniaPropertyChangedEventArgs)e;
+ result += ev.NewValue.Value.Int1;
+ }
+ else if (e.Property == TestClass.Struct6Property)
+ {
+ var ev = (AvaloniaPropertyChangedEventArgs)e;
+ result += ev.NewValue.Value.Int1;
+ }
+ else if (e.Property == TestClass.Struct7Property)
+ {
+ var ev = (AvaloniaPropertyChangedEventArgs)e;
+ result += ev.NewValue.Value.Int1;
+ }
+ else if (e.Property == TestClass.Struct8Property)
+ {
+ var ev = (AvaloniaPropertyChangedEventArgs)e;
+ result += ev.NewValue.Value.Int1;
+ }
+ }
+
+ target.PropertyChanged += ChangeHandler;
+
+ // GetObservable fires with the initial value so to compare like-for-like we also need
+ // to get the initial value here.
+ result += target.GetValue(TestClass.StringProperty)?.Length ?? 0;
+ result += target.GetValue(TestClass.Struct1Property).Int1;
+ result += target.GetValue(TestClass.Struct2Property).Int1;
+ result += target.GetValue(TestClass.Struct3Property).Int1;
+ result += target.GetValue(TestClass.Struct4Property).Int1;
+ result += target.GetValue(TestClass.Struct5Property).Int1;
+ result += target.GetValue(TestClass.Struct6Property).Int1;
+ result += target.GetValue(TestClass.Struct7Property).Int1;
+ result += target.GetValue(TestClass.Struct8Property).Int1;
+
+ for (var i = 0; i < 100; ++i)
+ {
+ target.SetValue(TestClass.StringProperty, "foo" + i);
+ target.SetValue(TestClass.Struct1Property, new(i + 1));
+ target.SetValue(TestClass.Struct2Property, new(i + 1));
+ target.SetValue(TestClass.Struct3Property, new(i + 1));
+ target.SetValue(TestClass.Struct4Property, new(i + 1));
+ target.SetValue(TestClass.Struct5Property, new(i + 1));
+ target.SetValue(TestClass.Struct6Property, new(i + 1));
+ target.SetValue(TestClass.Struct7Property, new(i + 1));
+ target.SetValue(TestClass.Struct8Property, new(i + 1));
+ }
+
+ target.PropertyChanged -= ChangeHandler;
+ }
+
+ [Benchmark]
+ public void GetObservables()
+ {
+ var target = _target;
+
+ var sub1 = target.GetObservable(TestClass.StringProperty).Subscribe(x => result += x?.Length ?? 0);
+ var sub2 = target.GetObservable(TestClass.Struct1Property).Subscribe(x => result += x.Int1);
+ var sub3 = target.GetObservable(TestClass.Struct2Property).Subscribe(x => result += x.Int1);
+ var sub4 = target.GetObservable(TestClass.Struct3Property).Subscribe(x => result += x.Int1);
+ var sub5 = target.GetObservable(TestClass.Struct4Property).Subscribe(x => result += x.Int1);
+ var sub6 = target.GetObservable(TestClass.Struct5Property).Subscribe(x => result += x.Int1);
+ var sub7 = target.GetObservable(TestClass.Struct6Property).Subscribe(x => result += x.Int1);
+ var sub8 = target.GetObservable(TestClass.Struct7Property).Subscribe(x => result += x.Int1);
+ var sub9 = target.GetObservable(TestClass.Struct8Property).Subscribe(x => result += x.Int1);
+
+ for (var i = 0; i < 100; ++i)
+ {
+ target.SetValue(TestClass.StringProperty, "foo" + i);
+ target.SetValue(TestClass.Struct1Property, new(i + 1));
+ target.SetValue(TestClass.Struct2Property, new(i + 1));
+ target.SetValue(TestClass.Struct3Property, new(i + 1));
+ target.SetValue(TestClass.Struct4Property, new(i + 1));
+ target.SetValue(TestClass.Struct5Property, new(i + 1));
+ target.SetValue(TestClass.Struct6Property, new(i + 1));
+ target.SetValue(TestClass.Struct7Property, new(i + 1));
+ target.SetValue(TestClass.Struct8Property, new(i + 1));
+ }
+
+ sub1.Dispose();
+ sub2.Dispose();
+ sub3.Dispose();
+ sub4.Dispose();
+ sub5.Dispose();
+ sub6.Dispose();
+ sub7.Dispose();
+ sub8.Dispose();
+ sub9.Dispose();
+ }
+
+ private class TestClass : AvaloniaObject
+ {
+ public static readonly StyledProperty StringProperty =
+ AvaloniaProperty.Register("String");
+ public static readonly StyledProperty Struct1Property =
+ AvaloniaProperty.Register("Struct1");
+ public static readonly StyledProperty Struct2Property =
+ AvaloniaProperty.Register("Struct2");
+ public static readonly StyledProperty Struct3Property =
+ AvaloniaProperty.Register("Struct3");
+ public static readonly StyledProperty Struct4Property =
+ AvaloniaProperty.Register("Struct4");
+ public static readonly StyledProperty Struct5Property =
+ AvaloniaProperty.Register("Struct5");
+ public static readonly StyledProperty Struct6Property =
+ AvaloniaProperty.Register("Struct6");
+ public static readonly StyledProperty Struct7Property =
+ AvaloniaProperty.Register("Struct7");
+ public static readonly StyledProperty Struct8Property =
+ AvaloniaProperty.Register("Struct8");
+ }
+ }
+}
diff --git a/tests/Avalonia.Benchmarks/Base/AvaloniaObject_GetValue.cs b/tests/Avalonia.Benchmarks/Base/AvaloniaObject_GetValue.cs
new file mode 100644
index 0000000000..0bfea0fafe
--- /dev/null
+++ b/tests/Avalonia.Benchmarks/Base/AvaloniaObject_GetValue.cs
@@ -0,0 +1,181 @@
+using System.Runtime.CompilerServices;
+using Avalonia.Data;
+using BenchmarkDotNet.Attributes;
+
+#nullable enable
+
+namespace Avalonia.Benchmarks.Base
+{
+ [MemoryDiagnoser]
+ public class AvaloniaObject_GetValue
+ {
+ private BaselineTestClass _baseline = new(){ StringProperty = "foo" };
+ private TestClass _target = new();
+
+ public AvaloniaObject_GetValue()
+ {
+ RuntimeHelpers.RunClassConstructor(typeof(TestClass).TypeHandle);
+ }
+
+ [Benchmark(Baseline = true)]
+ public int GetClrPropertyValues()
+ {
+ var target = _baseline;
+ var result = 0;
+
+ for (var i = 0; i < 100; ++i)
+ {
+ result += target.StringProperty?.Length ?? 0;
+ result += target.Struct1Property.Int1;
+ result += target.Struct2Property.Int1;
+ result += target.Struct3Property.Int1;
+ result += target.Struct4Property.Int1;
+ result += target.Struct5Property.Int1;
+ result += target.Struct6Property.Int1;
+ result += target.Struct7Property.Int1;
+ result += target.Struct8Property.Int1;
+ }
+
+ return result;
+ }
+
+ [Benchmark]
+ public int GetDefaultValues()
+ {
+ var target = _target;
+ var result = 0;
+
+ for (var i = 0; i < 100; ++i)
+ {
+ result += target.GetValue(TestClass.StringProperty)?.Length ?? 0;
+ result += target.GetValue(TestClass.Struct1Property).Int1;
+ result += target.GetValue(TestClass.Struct2Property).Int1;
+ result += target.GetValue(TestClass.Struct3Property).Int1;
+ result += target.GetValue(TestClass.Struct4Property).Int1;
+ result += target.GetValue(TestClass.Struct5Property).Int1;
+ result += target.GetValue(TestClass.Struct6Property).Int1;
+ result += target.GetValue(TestClass.Struct7Property).Int1;
+ result += target.GetValue(TestClass.Struct8Property).Int1;
+ }
+
+ return result;
+ }
+
+ [GlobalSetup(Target = nameof(Get_Local_Values))]
+ public void SetupLocalValues()
+ {
+ _target.SetValue(TestClass.StringProperty, "foo");
+ _target.SetValue(TestClass.Struct1Property, new(1));
+ _target.SetValue(TestClass.Struct2Property, new(1));
+ _target.SetValue(TestClass.Struct3Property, new(1));
+ _target.SetValue(TestClass.Struct4Property, new(1));
+ _target.SetValue(TestClass.Struct5Property, new(1));
+ _target.SetValue(TestClass.Struct6Property, new(1));
+ _target.SetValue(TestClass.Struct7Property, new(1));
+ _target.SetValue(TestClass.Struct8Property, new(1));
+ }
+
+ [Benchmark]
+ public int Get_Local_Values()
+ {
+ var target = _target;
+ var result = 0;
+
+ for (var i = 0; i < 100; ++i)
+ {
+ result += target.GetValue(TestClass.StringProperty)?.Length ?? 0;
+ result += target.GetValue(TestClass.Struct1Property).Int1;
+ result += target.GetValue(TestClass.Struct2Property).Int1;
+ result += target.GetValue(TestClass.Struct3Property).Int1;
+ result += target.GetValue(TestClass.Struct4Property).Int1;
+ result += target.GetValue(TestClass.Struct5Property).Int1;
+ result += target.GetValue(TestClass.Struct6Property).Int1;
+ result += target.GetValue(TestClass.Struct7Property).Int1;
+ result += target.GetValue(TestClass.Struct8Property).Int1;
+ }
+
+ return result;
+ }
+
+ [GlobalSetup(Target = nameof(Get_Local_Values_With_Style_Values))]
+ public void SetupLocalValuesAndStyleValues()
+ {
+ var target = _target;
+ target.SetValue(TestClass.StringProperty, "foo");
+ target.SetValue(TestClass.Struct1Property, new(1));
+ target.SetValue(TestClass.Struct2Property, new(1));
+ target.SetValue(TestClass.Struct3Property, new(1));
+ target.SetValue(TestClass.Struct4Property, new(1));
+ target.SetValue(TestClass.Struct5Property, new(1));
+ target.SetValue(TestClass.Struct6Property, new(1));
+ target.SetValue(TestClass.Struct7Property, new(1));
+ target.SetValue(TestClass.Struct8Property, new(1));
+ target.SetValue(TestClass.StringProperty, "bar", BindingPriority.Style);
+ target.SetValue(TestClass.Struct1Property, new(), BindingPriority.Style);
+ target.SetValue(TestClass.Struct2Property, new(), BindingPriority.Style);
+ target.SetValue(TestClass.Struct3Property, new(), BindingPriority.Style);
+ target.SetValue(TestClass.Struct4Property, new(), BindingPriority.Style);
+ target.SetValue(TestClass.Struct5Property, new(), BindingPriority.Style);
+ target.SetValue(TestClass.Struct6Property, new(), BindingPriority.Style);
+ target.SetValue(TestClass.Struct7Property, new(), BindingPriority.Style);
+ target.SetValue(TestClass.Struct8Property, new(), BindingPriority.Style);
+ }
+
+ [Benchmark]
+ public int Get_Local_Values_With_Style_Values()
+ {
+ var target = _target;
+ var result = 0;
+
+ for (var i = 0; i < 100; ++i)
+ {
+ result += target.GetValue(TestClass.StringProperty)?.Length ?? 0;
+ result += target.GetValue(TestClass.Struct1Property).Int1;
+ result += target.GetValue(TestClass.Struct2Property).Int1;
+ result += target.GetValue(TestClass.Struct3Property).Int1;
+ result += target.GetValue(TestClass.Struct4Property).Int1;
+ result += target.GetValue(TestClass.Struct5Property).Int1;
+ result += target.GetValue(TestClass.Struct6Property).Int1;
+ result += target.GetValue(TestClass.Struct7Property).Int1;
+ result += target.GetValue(TestClass.Struct8Property).Int1;
+ }
+
+ return result;
+ }
+
+ private class TestClass : AvaloniaObject
+ {
+ public static readonly StyledProperty StringProperty =
+ AvaloniaProperty.Register("String");
+ public static readonly StyledProperty Struct1Property =
+ AvaloniaProperty.Register("Struct1");
+ public static readonly StyledProperty Struct2Property =
+ AvaloniaProperty.Register("Struct2");
+ public static readonly StyledProperty Struct3Property =
+ AvaloniaProperty.Register("Struct3");
+ public static readonly StyledProperty Struct4Property =
+ AvaloniaProperty.Register("Struct4");
+ public static readonly StyledProperty Struct5Property =
+ AvaloniaProperty.Register("Struct5");
+ public static readonly StyledProperty Struct6Property =
+ AvaloniaProperty.Register("Struct6");
+ public static readonly StyledProperty Struct7Property =
+ AvaloniaProperty.Register("Struct7");
+ public static readonly StyledProperty Struct8Property =
+ AvaloniaProperty.Register("Struct8");
+ }
+
+ private class BaselineTestClass
+ {
+ public string? StringProperty { get; set; }
+ public Struct1 Struct1Property { get; set; }
+ public Struct2 Struct2Property { get; set; }
+ public Struct3 Struct3Property { get; set; }
+ public Struct4 Struct4Property { get; set; }
+ public Struct5 Struct5Property { get; set; }
+ public Struct6 Struct6Property { get; set; }
+ public Struct7 Struct7Property { get; set; }
+ public Struct8 Struct8Property { get; set; }
+ }
+ }
+}
diff --git a/tests/Avalonia.Benchmarks/Base/AvaloniaObject_GetValueInherited.cs b/tests/Avalonia.Benchmarks/Base/AvaloniaObject_GetValueInherited.cs
new file mode 100644
index 0000000000..45e129ecd8
--- /dev/null
+++ b/tests/Avalonia.Benchmarks/Base/AvaloniaObject_GetValueInherited.cs
@@ -0,0 +1,95 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+using Avalonia.Controls;
+using BenchmarkDotNet.Attributes;
+
+#nullable enable
+
+namespace Avalonia.Benchmarks.Base
+{
+ [MemoryDiagnoser]
+ public class AvaloniaObject_GetValueInherited
+ {
+ private TestClass _root = null!;
+ private TestClass _target = null!;
+
+ public AvaloniaObject_GetValueInherited()
+ {
+ RuntimeHelpers.RunClassConstructor(typeof(TestClass).TypeHandle);
+ }
+
+ [Params(1, 2, 10, 50, 100, 200)]
+ public int Depth { get; set; }
+
+ [GlobalSetup]
+ public void Setup()
+ {
+ _root = new();
+ _root.SetValue(TestClass.StringProperty, "foo");
+ _root.SetValue(TestClass.Struct1Property, new(1));
+ _root.SetValue(TestClass.Struct2Property, new(1));
+ _root.SetValue(TestClass.Struct3Property, new(1));
+ _root.SetValue(TestClass.Struct4Property, new(1));
+ _root.SetValue(TestClass.Struct5Property, new(1));
+ _root.SetValue(TestClass.Struct6Property, new(1));
+ _root.SetValue(TestClass.Struct7Property, new(1));
+ _root.SetValue(TestClass.Struct8Property, new(1));
+
+ var parent = _root;
+
+ for (var i = 0; i < Depth; ++i)
+ {
+ var c = new TestClass();
+ ((ISetLogicalParent)c).SetParent(parent);
+ parent = c;
+ }
+
+ _target = parent;
+ }
+
+ [Benchmark]
+ public int GetInheritedValues()
+ {
+ var target = _target;
+ var result = 0;
+
+ for (var i = 0; i < 100; ++i)
+ {
+ result += target.GetValue(TestClass.StringProperty)?.Length ?? 0;
+ result += target.GetValue(TestClass.Struct1Property).Int1;
+ result += target.GetValue(TestClass.Struct2Property).Int1;
+ result += target.GetValue(TestClass.Struct3Property).Int1;
+ result += target.GetValue(TestClass.Struct4Property).Int1;
+ result += target.GetValue(TestClass.Struct5Property).Int1;
+ result += target.GetValue(TestClass.Struct6Property).Int1;
+ result += target.GetValue(TestClass.Struct7Property).Int1;
+ result += target.GetValue(TestClass.Struct8Property).Int1;
+ }
+
+ return result;
+ }
+
+ private class TestClass : Control
+ {
+ public static readonly StyledProperty StringProperty =
+ AvaloniaProperty.Register("String", inherits: true);
+ public static readonly StyledProperty Struct1Property =
+ AvaloniaProperty.Register("Struct1", inherits: true);
+ public static readonly StyledProperty Struct2Property =
+ AvaloniaProperty.Register("Struct2", inherits: true);
+ public static readonly StyledProperty Struct3Property =
+ AvaloniaProperty.Register("Struct3", inherits: true);
+ public static readonly StyledProperty Struct4Property =
+ AvaloniaProperty.Register("Struct4", inherits: true);
+ public static readonly StyledProperty Struct5Property =
+ AvaloniaProperty.Register("Struct5", inherits: true);
+ public static readonly StyledProperty Struct6Property =
+ AvaloniaProperty.Register("Struct6", inherits: true);
+ public static readonly StyledProperty Struct7Property =
+ AvaloniaProperty.Register("Struct7", inherits: true);
+ public static readonly StyledProperty Struct8Property =
+ AvaloniaProperty.Register("Struct8", inherits: true);
+ }
+ }
+}
diff --git a/tests/Avalonia.Benchmarks/Base/AvaloniaObject_SetValue.cs b/tests/Avalonia.Benchmarks/Base/AvaloniaObject_SetValue.cs
new file mode 100644
index 0000000000..2df265984b
--- /dev/null
+++ b/tests/Avalonia.Benchmarks/Base/AvaloniaObject_SetValue.cs
@@ -0,0 +1,130 @@
+using System.Runtime.CompilerServices;
+using Avalonia.Data;
+using BenchmarkDotNet.Attributes;
+
+#nullable enable
+
+namespace Avalonia.Benchmarks.Base
+{
+ [MemoryDiagnoser]
+ public class AvaloniaObject_SetValue
+ {
+ private BaselineTestClass _baseline = new();
+ private TestClass _target = new();
+
+ public AvaloniaObject_SetValue()
+ {
+ RuntimeHelpers.RunClassConstructor(typeof(TestClass).TypeHandle);
+ }
+
+ [Benchmark(Baseline = true)]
+ public int SetClrPropertyValues()
+ {
+ var target = _baseline;
+ var result = 0;
+
+ for (var i = 0; i < 100; ++i)
+ {
+ target.StringProperty = "foo";
+ target.Struct1Property = new(i + 1);
+ target.Struct2Property = new(i + 1);
+ target.Struct3Property = new(i + 1);
+ target.Struct4Property = new(i + 1);
+ target.Struct5Property = new(i + 1);
+ target.Struct6Property = new(i + 1);
+ target.Struct7Property = new(i + 1);
+ target.Struct8Property = new(i + 1);
+ }
+
+ return result;
+ }
+
+ [Benchmark]
+ public void SetValues()
+ {
+ var target = _target;
+
+ for (var i = 0; i < 100; ++i)
+ {
+ target.SetValue(TestClass.StringProperty, "foo");
+ target.SetValue(TestClass.Struct1Property, new(i + 1));
+ target.SetValue(TestClass.Struct2Property, new(i + 1));
+ target.SetValue(TestClass.Struct3Property, new(i + 1));
+ target.SetValue(TestClass.Struct4Property, new(i + 1));
+ target.SetValue(TestClass.Struct5Property, new(i + 1));
+ target.SetValue(TestClass.Struct6Property, new(i + 1));
+ target.SetValue(TestClass.Struct7Property, new(i + 1));
+ target.SetValue(TestClass.Struct8Property, new(i + 1));
+ }
+ }
+
+ [GlobalSetup(Target = nameof(Set_Local_Values_With_Style_Values))]
+ public void SetupStyleValues()
+ {
+ var target = _target;
+ target.SetValue(TestClass.StringProperty, "foo", BindingPriority.Style);
+ target.SetValue(TestClass.Struct1Property, new(), BindingPriority.Style);
+ target.SetValue(TestClass.Struct2Property, new(), BindingPriority.Style);
+ target.SetValue(TestClass.Struct3Property, new(), BindingPriority.Style);
+ target.SetValue(TestClass.Struct4Property, new(), BindingPriority.Style);
+ target.SetValue(TestClass.Struct5Property, new(), BindingPriority.Style);
+ target.SetValue(TestClass.Struct6Property, new(), BindingPriority.Style);
+ target.SetValue(TestClass.Struct7Property, new(), BindingPriority.Style);
+ target.SetValue(TestClass.Struct8Property, new(), BindingPriority.Style);
+ }
+
+ [Benchmark]
+ public void Set_Local_Values_With_Style_Values()
+ {
+ var target = _target;
+
+ for (var i = 0; i < 100; ++i)
+ {
+ target.SetValue(TestClass.StringProperty, "foo");
+ target.SetValue(TestClass.Struct1Property, new(i + 1));
+ target.SetValue(TestClass.Struct2Property, new(i + 1));
+ target.SetValue(TestClass.Struct3Property, new(i + 1));
+ target.SetValue(TestClass.Struct4Property, new(i + 1));
+ target.SetValue(TestClass.Struct5Property, new(i + 1));
+ target.SetValue(TestClass.Struct6Property, new(i + 1));
+ target.SetValue(TestClass.Struct7Property, new(i + 1));
+ target.SetValue(TestClass.Struct8Property, new(i + 1));
+ }
+ }
+
+ private class TestClass : AvaloniaObject
+ {
+ public static readonly StyledProperty StringProperty =
+ AvaloniaProperty.Register("String");
+ public static readonly StyledProperty Struct1Property =
+ AvaloniaProperty.Register("Struct1");
+ public static readonly StyledProperty Struct2Property =
+ AvaloniaProperty.Register("Struct2");
+ public static readonly StyledProperty Struct3Property =
+ AvaloniaProperty.Register("Struct3");
+ public static readonly StyledProperty Struct4Property =
+ AvaloniaProperty.Register("Struct4");
+ public static readonly StyledProperty Struct5Property =
+ AvaloniaProperty.Register("Struct5");
+ public static readonly StyledProperty Struct6Property =
+ AvaloniaProperty.Register("Struct6");
+ public static readonly StyledProperty Struct7Property =
+ AvaloniaProperty.Register("Struct7");
+ public static readonly StyledProperty Struct8Property =
+ AvaloniaProperty.Register("Struct8");
+ }
+
+ private class BaselineTestClass
+ {
+ public string? StringProperty { get; set; }
+ public Struct1 Struct1Property { get; set; }
+ public Struct2 Struct2Property { get; set; }
+ public Struct3 Struct3Property { get; set; }
+ public Struct4 Struct4Property { get; set; }
+ public Struct5 Struct5Property { get; set; }
+ public Struct6 Struct6Property { get; set; }
+ public Struct7 Struct7Property { get; set; }
+ public Struct8 Struct8Property { get; set; }
+ }
+ }
+}
diff --git a/tests/Avalonia.Benchmarks/Base/DirectPropertyBenchmark.cs b/tests/Avalonia.Benchmarks/Base/DirectPropertyBenchmark.cs
index 02f9397e2c..e823b29526 100644
--- a/tests/Avalonia.Benchmarks/Base/DirectPropertyBenchmark.cs
+++ b/tests/Avalonia.Benchmarks/Base/DirectPropertyBenchmark.cs
@@ -1,14 +1,22 @@
-using BenchmarkDotNet.Attributes;
+using System.Runtime.CompilerServices;
+using BenchmarkDotNet.Attributes;
namespace Avalonia.Benchmarks.Base
{
[MemoryDiagnoser]
public class DirectPropertyBenchmark
{
+ private DirectClass _target = new();
+
+ public DirectPropertyBenchmark()
+ {
+ RuntimeHelpers.RunClassConstructor(typeof(DirectClass).TypeHandle);
+ }
+
[Benchmark(Baseline = true)]
public void SetAndRaiseOriginal()
{
- var obj = new DirectClass();
+ var obj = _target;
for (var i = 0; i < 100; ++i)
{
@@ -19,7 +27,7 @@ namespace Avalonia.Benchmarks.Base
[Benchmark]
public void SetAndRaiseSimple()
{
- var obj = new DirectClass();
+ var obj = _target;
for (var i = 0; i < 100; ++i)
{
diff --git a/tests/Avalonia.Benchmarks/Styling/Style_Activation.cs b/tests/Avalonia.Benchmarks/Styling/Style_Activation.cs
new file mode 100644
index 0000000000..f5878286e7
--- /dev/null
+++ b/tests/Avalonia.Benchmarks/Styling/Style_Activation.cs
@@ -0,0 +1,65 @@
+using System.Runtime.CompilerServices;
+using Avalonia.Controls;
+using Avalonia.Styling;
+using BenchmarkDotNet.Attributes;
+
+#nullable enable
+
+namespace Avalonia.Benchmarks.Styling
+{
+ [MemoryDiagnoser]
+ public class Style_Activation
+ {
+ private TestClass _target = null!;
+
+ public Style_Activation()
+ {
+ RuntimeHelpers.RunClassConstructor(typeof(TestClass).TypeHandle);
+ }
+
+ [GlobalSetup]
+ public void Setup()
+ {
+ _target = new TestClass();
+
+ var style = new Style(x => x.OfType().Class("foo"))
+ {
+ Setters = { new Setter(TestClass.StringProperty, "foo") }
+ };
+
+ style.TryAttach(_target, null);
+ }
+
+ [Benchmark]
+ public void Toggle_Style_Activation_Via_Class()
+ {
+ for (var i = 0; i < 100; ++i)
+ {
+ _target.Classes.Add("foo");
+ _target.Classes.Remove("foo");
+ }
+ }
+
+ private class TestClass : Control
+ {
+ public static readonly StyledProperty StringProperty =
+ AvaloniaProperty.Register("String");
+ public static readonly StyledProperty Struct1Property =
+ AvaloniaProperty.Register("Struct1");
+ public static readonly StyledProperty Struct2Property =
+ AvaloniaProperty.Register("Struct2");
+ public static readonly StyledProperty Struct3Property =
+ AvaloniaProperty.Register("Struct3");
+ public static readonly StyledProperty Struct4Property =
+ AvaloniaProperty.Register("Struct4");
+ public static readonly StyledProperty Struct5Property =
+ AvaloniaProperty.Register("Struct5");
+ public static readonly StyledProperty Struct6Property =
+ AvaloniaProperty.Register("Struct6");
+ public static readonly StyledProperty Struct7Property =
+ AvaloniaProperty.Register("Struct7");
+ public static readonly StyledProperty Struct8Property =
+ AvaloniaProperty.Register("Struct8");
+ }
+ }
+}
diff --git a/tests/Avalonia.Benchmarks/Styling/Style_Apply.cs b/tests/Avalonia.Benchmarks/Styling/Style_Apply.cs
new file mode 100644
index 0000000000..2943eef55a
--- /dev/null
+++ b/tests/Avalonia.Benchmarks/Styling/Style_Apply.cs
@@ -0,0 +1,89 @@
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+using Avalonia.Controls;
+using Avalonia.Styling;
+using BenchmarkDotNet.Attributes;
+
+#nullable enable
+
+namespace Avalonia.Benchmarks.Styling
+{
+ [MemoryDiagnoser]
+ public class Style_Apply
+ {
+ private List