Skip to content

Commit aff89d4

Browse files
maca88fredericDelaporte
authored andcommitted
Extend IAccessOptimizer to support getting/setting single property value (#1944)
1 parent dfd9c3b commit aff89d4

14 files changed

+584
-33
lines changed

src/NHibernate.Test/NHSpecificTest/NH3119/FixtureByCode.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ public class ComponentTestReflectionOptimizer : ReflectionOptimizer
131131
public static bool IsCalledForComponent { get; set; }
132132

133133
public ComponentTestReflectionOptimizer(System.Type mappedType, IGetter[] getters, ISetter[] setters) :
134-
base(mappedType, getters, setters)
134+
base(mappedType, getters, setters, null, null)
135135
{
136136
_logCall = mappedType == typeof(Component);
137137
}
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Diagnostics;
4+
using System.IO;
5+
using NHibernate.Bytecode;
6+
using NHibernate.Bytecode.Lightweight;
7+
using NHibernate.Properties;
8+
using NUnit.Framework;
9+
10+
namespace NHibernate.Test.PropertyTest
11+
{
12+
public abstract class AccessorPerformanceFixture<T> where T : new()
13+
{
14+
private IPropertyAccessor _accessor;
15+
private IAccessOptimizer _optimizer;
16+
private ISetter[] _setters;
17+
private IGetter[] _getters;
18+
19+
protected abstract string AccessorType { get; }
20+
21+
protected abstract List<string> PropertyNames { get; }
22+
23+
protected abstract object[] GetValues();
24+
25+
[SetUp]
26+
public void SetUp()
27+
{
28+
_accessor = PropertyAccessorFactory.GetPropertyAccessor(AccessorType);
29+
30+
_getters = new IGetter[PropertyNames.Count];
31+
_setters = new ISetter[PropertyNames.Count];
32+
var type = typeof(T);
33+
34+
for (var i = 0; i < PropertyNames.Count; i++)
35+
{
36+
_getters[i] = _accessor.GetGetter(type, PropertyNames[i]);
37+
_setters[i] = _accessor.GetSetter(type, PropertyNames[i]);
38+
}
39+
40+
_optimizer = new ReflectionOptimizer(type, _getters, _setters, null, null).AccessOptimizer;
41+
}
42+
43+
[TestCase(50000)]
44+
[TestCase(100000)]
45+
[TestCase(200000)]
46+
[TestCase(500000)]
47+
public void TestGetter(int iter)
48+
{
49+
var target = new T();
50+
var stopwatch = new Stopwatch();
51+
52+
// Warm up
53+
TestGetter(target, 100);
54+
TestOptimizedGetter(target, 100);
55+
TestOptimizedMultiGetter(target, 100);
56+
57+
stopwatch.Restart();
58+
TestGetter(target, iter);
59+
stopwatch.Stop();
60+
Console.WriteLine($"IGetter.Get total time for {iter} iterations: {stopwatch.ElapsedMilliseconds}ms");
61+
62+
stopwatch.Restart();
63+
TestOptimizedGetter(target, iter);
64+
stopwatch.Stop();
65+
Console.WriteLine($"IAccessOptimizer.GetPropertyValue total time for {iter} iterations: {stopwatch.ElapsedMilliseconds}ms");
66+
67+
stopwatch.Restart();
68+
TestOptimizedMultiGetter(target, iter);
69+
stopwatch.Stop();
70+
Console.WriteLine($"IAccessOptimizer.GetPropertyValues total time for {iter} iterations: {stopwatch.ElapsedMilliseconds}ms");
71+
}
72+
73+
[TestCase(50000)]
74+
[TestCase(100000)]
75+
[TestCase(200000)]
76+
[TestCase(500000)]
77+
public void TestSetter(int iter)
78+
{
79+
var target = new T();
80+
var stopwatch = new Stopwatch();
81+
82+
// Warm up
83+
TestSetter(target, 100);
84+
TestOptimizedSetter(target, 100);
85+
TestOptimizedMultiSetter(target, 100);
86+
87+
stopwatch.Restart();
88+
TestSetter(target, iter);
89+
stopwatch.Stop();
90+
Console.WriteLine($"ISetter.Set total time for {iter} iterations: {stopwatch.ElapsedMilliseconds}ms");
91+
92+
stopwatch.Restart();
93+
TestOptimizedSetter(target, iter);
94+
stopwatch.Stop();
95+
Console.WriteLine($"IAccessOptimizer.SetPropertyValue total time for {iter} iterations: {stopwatch.ElapsedMilliseconds}ms");
96+
97+
stopwatch.Restart();
98+
TestOptimizedMultiSetter(target, iter);
99+
stopwatch.Stop();
100+
Console.WriteLine($"IAccessOptimizer.SetPropertyValues total time for {iter} iterations: {stopwatch.ElapsedMilliseconds}ms");
101+
}
102+
103+
private void TestGetter(object target, int iter)
104+
{
105+
for (var i = 0; i < iter; i++)
106+
{
107+
for (var j = 0; j < _getters.Length; j++)
108+
{
109+
var val = _getters[j].Get(target);
110+
}
111+
}
112+
}
113+
114+
private void TestOptimizedGetter(object target, int iter)
115+
{
116+
for (var i = 0; i < iter; i++)
117+
{
118+
for (var j = 0; j < _getters.Length; j++)
119+
{
120+
var val = _optimizer.GetPropertyValue(target, j);
121+
}
122+
}
123+
}
124+
125+
private void TestOptimizedMultiGetter(object target, int iter)
126+
{
127+
for (var i = 0; i < iter; i++)
128+
{
129+
var val = _optimizer.GetPropertyValues(target);
130+
}
131+
}
132+
133+
private void TestSetter(object target, int iter)
134+
{
135+
for (var i = 0; i < iter; i++)
136+
{
137+
var values = GetValues();
138+
for (var j = 0; j < _setters.Length; j++)
139+
{
140+
_setters[j].Set(target, values[j]);
141+
}
142+
}
143+
}
144+
145+
private void TestOptimizedSetter(object target, int iter)
146+
{
147+
for (var i = 0; i < iter; i++)
148+
{
149+
var values = GetValues();
150+
for (var j = 0; j < _setters.Length; j++)
151+
{
152+
_optimizer.SetPropertyValue(target, j, values[j]);
153+
}
154+
}
155+
}
156+
157+
private void TestOptimizedMultiSetter(object target, int iter)
158+
{
159+
for (var i = 0; i < iter; i++)
160+
{
161+
_optimizer.SetPropertyValues(target, GetValues());
162+
}
163+
}
164+
}
165+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using NUnit.Framework;
4+
5+
namespace NHibernate.Test.PropertyTest
6+
{
7+
[TestFixture, Explicit]
8+
public class BasicPropertyAccessorPerformanceFixture : AccessorPerformanceFixture<BasicPropertyAccessorPerformanceFixture.A>
9+
{
10+
protected override string AccessorType => "property";
11+
12+
protected override List<string> PropertyNames => new List<string>
13+
{
14+
nameof(A.Id),
15+
nameof(A.Name),
16+
nameof(A.Date),
17+
nameof(A.Decimal)
18+
};
19+
20+
protected override object[] GetValues()
21+
{
22+
return new object[] { 5, "name", DateTime.MaxValue, 1.5m };
23+
}
24+
25+
public class A
26+
{
27+
public int Id { get; set; }
28+
29+
public string Name { get; set; }
30+
31+
public DateTime Date { get; set; }
32+
33+
public decimal? Decimal { get; set; }
34+
}
35+
}
36+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using NUnit.Framework;
4+
5+
namespace NHibernate.Test.PropertyTest
6+
{
7+
[TestFixture, Explicit]
8+
public class FieldAccessorPerformanceFixture : AccessorPerformanceFixture<FieldAccessorPerformanceFixture.A>
9+
{
10+
protected override string AccessorType => "field";
11+
12+
protected override List<string> PropertyNames => new List<string>
13+
{
14+
"_id",
15+
"_name",
16+
"_date",
17+
"_decimal",
18+
};
19+
20+
protected override object[] GetValues()
21+
{
22+
return new object[] {5, "name", DateTime.MaxValue, 1.5m};
23+
}
24+
25+
public class A
26+
{
27+
private int _id = 5;
28+
private string _name =string.Empty;
29+
private DateTime _date = DateTime.MinValue;
30+
private decimal? _decimal = decimal.Zero;
31+
32+
public int Id => _id;
33+
34+
public string Name => _name;
35+
36+
public DateTime Date => _date;
37+
38+
public decimal? Decimal => _decimal;
39+
}
40+
}
41+
}

src/NHibernate.Test/ReflectionOptimizerTest/LcgFixture.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public void NoSetter()
2929
new BasicPropertyAccessor.BasicSetter(typeof (NoSetterClass), typeof (NoSetterClass).GetProperty("Property"), "Property")
3030
};
3131

32-
Assert.Throws<PropertyNotFoundException>(() => new ReflectionOptimizer(typeof(NoSetterClass), getters, setters));
32+
Assert.Throws<PropertyNotFoundException>(() => new ReflectionOptimizer(typeof(NoSetterClass), getters, setters, null, null));
3333
}
3434

3535
public class NoGetterClass
@@ -52,7 +52,7 @@ public void NoGetter()
5252
new BasicPropertyAccessor.BasicSetter(typeof (NoGetterClass), typeof (NoGetterClass).GetProperty("Property"), "Property")
5353
};
5454

55-
Assert.Throws<PropertyNotFoundException>(() => new ReflectionOptimizer(typeof (NoGetterClass), getters, setters));
55+
Assert.Throws<PropertyNotFoundException>(() => new ReflectionOptimizer(typeof (NoGetterClass), getters, setters, null, null));
5656
}
5757

5858
public class GetterTypeMismatchClass
@@ -83,7 +83,9 @@ public void TestGetterTypeMismatch()
8383
ReflectionOptimizer reflectionOptimizer = new ReflectionOptimizer(
8484
obj.GetType(),
8585
new[] { accessor.GetGetter(obj.GetType(), property) },
86-
new[] { accessor.GetSetter(obj.GetType(), property) });
86+
new[] { accessor.GetSetter(obj.GetType(), property) },
87+
null,
88+
null);
8789

8890
IAccessOptimizer accessOptimizer = reflectionOptimizer.AccessOptimizer;
8991

src/NHibernate/Bytecode/IAccessOptimizer.cs

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
using System;
2+
using NHibernate.Bytecode.Lightweight;
3+
14
namespace NHibernate.Bytecode
25
{
36
/// <summary>
@@ -8,4 +11,65 @@ public interface IAccessOptimizer
811
object[] GetPropertyValues(object target);
912
void SetPropertyValues(object target, object[] values);
1013
}
11-
}
14+
15+
public static class AccessOptimizerExtensions
16+
{
17+
/// <summary>
18+
/// Get the property value on the given index.
19+
/// </summary>
20+
//6.0 TODO: Merge into IAccessOptimizer.
21+
public static object GetPropertyValue(this IAccessOptimizer optimizer, object target, int i)
22+
{
23+
if (optimizer is AccessOptimizer accessOptimizer)
24+
{
25+
return accessOptimizer.GetPropertyValue(target, i);
26+
}
27+
28+
throw new InvalidOperationException($"{optimizer.GetType()} does not support {nameof(GetPropertyValue)} method.");
29+
}
30+
31+
/// <summary>
32+
/// Set the property value on the given index.
33+
/// </summary>
34+
//6.0 TODO: Merge into IAccessOptimizer.
35+
public static void SetPropertyValue(this IAccessOptimizer optimizer, object target, int i, object value)
36+
{
37+
if (optimizer is AccessOptimizer accessOptimizer)
38+
{
39+
accessOptimizer.SetPropertyValue(target, i, value);
40+
return;
41+
}
42+
43+
throw new InvalidOperationException($"{optimizer.GetType()} does not support {nameof(SetPropertyValue)} method.");
44+
}
45+
46+
/// <summary>
47+
/// Get the specialized property value.
48+
/// </summary>
49+
//6.0 TODO: Merge into IAccessOptimizer.
50+
internal static object GetSpecializedPropertyValue(this IAccessOptimizer optimizer, object target)
51+
{
52+
if (optimizer is AccessOptimizer accessOptimizer)
53+
{
54+
return accessOptimizer.GetSpecializedPropertyValue(target);
55+
}
56+
57+
throw new InvalidOperationException($"{optimizer.GetType()} does not support {nameof(GetPropertyValue)} method.");
58+
}
59+
60+
/// <summary>
61+
/// Set the specialized property value.
62+
/// </summary>
63+
//6.0 TODO: Merge into IAccessOptimizer.
64+
internal static void SetSpecializedPropertyValue(this IAccessOptimizer optimizer, object target, object value)
65+
{
66+
if (optimizer is AccessOptimizer accessOptimizer)
67+
{
68+
accessOptimizer.SetSpecializedPropertyValue(target, value);
69+
return;
70+
}
71+
72+
throw new InvalidOperationException($"{optimizer.GetType()} does not support {nameof(SetPropertyValue)} method.");
73+
}
74+
}
75+
}

0 commit comments

Comments
 (0)