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