Skip to content

Commit 4edb066

Browse files
committed
Extend IAccessOptimizer to support getting/setting single property value
1 parent 4d887d8 commit 4edb066

17 files changed

+675
-30
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: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
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 GetValue(int i);
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 TestGetPropertyValue(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+
56+
stopwatch.Restart();
57+
TestGetter(target, iter);
58+
stopwatch.Stop();
59+
Console.WriteLine($"Reflection getter total time for {iter} iterations: {stopwatch.ElapsedMilliseconds}ms");
60+
61+
stopwatch.Restart();
62+
TestOptimizedGetter(target, iter);
63+
stopwatch.Stop();
64+
Console.WriteLine($"IL getter total time for {iter} iterations: {stopwatch.ElapsedMilliseconds}ms");
65+
}
66+
67+
[TestCase(50000)]
68+
[TestCase(100000)]
69+
[TestCase(200000)]
70+
[TestCase(500000)]
71+
public void TestSetPropertyValue(int iter)
72+
{
73+
var target = new T();
74+
var stopwatch = new Stopwatch();
75+
76+
// Warm up
77+
TestSetter(target, 100);
78+
TestOptimizedSetter(target, 100);
79+
80+
stopwatch.Restart();
81+
TestSetter(target, iter);
82+
stopwatch.Stop();
83+
Console.WriteLine($"Reflection setter total time for {iter} iterations: {stopwatch.ElapsedMilliseconds}ms");
84+
85+
stopwatch.Restart();
86+
TestOptimizedSetter(target, iter);
87+
stopwatch.Stop();
88+
Console.WriteLine($"IL setter total time for {iter} iterations: {stopwatch.ElapsedMilliseconds}ms");
89+
}
90+
91+
private void TestGetter(object target, int iter)
92+
{
93+
for (var i = 0; i < iter; i++)
94+
{
95+
for (var j = 0; j < _getters.Length; j++)
96+
{
97+
var val = _getters[j].Get(target);
98+
}
99+
}
100+
}
101+
102+
private void TestOptimizedGetter(object target, int iter)
103+
{
104+
for (var i = 0; i < iter; i++)
105+
{
106+
for (var j = 0; j < _getters.Length; j++)
107+
{
108+
var val = _optimizer.GetPropertyValue(target, j);
109+
}
110+
}
111+
}
112+
113+
private void TestSetter(object target, int iter)
114+
{
115+
for (var i = 0; i < iter; i++)
116+
{
117+
for (var j = 0; j < _setters.Length; j++)
118+
{
119+
_setters[j].Set(target, GetValue(j));
120+
}
121+
}
122+
}
123+
124+
private void TestOptimizedSetter(object target, int iter)
125+
{
126+
for (var i = 0; i < iter; i++)
127+
{
128+
for (var j = 0; j < _setters.Length; j++)
129+
{
130+
_optimizer.SetPropertyValue(target, j, GetValue(j));
131+
}
132+
}
133+
}
134+
}
135+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using System.Collections.Generic;
2+
using NUnit.Framework;
3+
4+
namespace NHibernate.Test.PropertyTest
5+
{
6+
[TestFixture, Explicit]
7+
public class BasicPropertyAccessorPerformanceFixture : AccessorPerformanceFixture<BasicPropertyAccessorPerformanceFixture.A>
8+
{
9+
protected override string AccessorType => "property";
10+
11+
protected override List<string> PropertyNames => new List<string>
12+
{
13+
nameof(A.Id),
14+
nameof(A.Name)
15+
};
16+
17+
protected override object GetValue(int i)
18+
{
19+
switch (i)
20+
{
21+
case 0:
22+
return 5;
23+
case 1:
24+
return "name";
25+
}
26+
27+
return null;
28+
}
29+
30+
public class A
31+
{
32+
public int Id { get; set; }
33+
34+
public string Name { get; set; }
35+
}
36+
}
37+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using System.Collections.Generic;
2+
using NUnit.Framework;
3+
4+
namespace NHibernate.Test.PropertyTest
5+
{
6+
[TestFixture, Explicit]
7+
public class FieldAccessorPerformanceFixture : AccessorPerformanceFixture<FieldAccessorPerformanceFixture.A>
8+
{
9+
protected override string AccessorType => "field";
10+
11+
protected override List<string> PropertyNames => new List<string>
12+
{
13+
"_id",
14+
"_name"
15+
};
16+
17+
protected override object GetValue(int i)
18+
{
19+
switch (i)
20+
{
21+
case 0:
22+
return 5;
23+
case 1:
24+
return "name";
25+
}
26+
27+
return null;
28+
}
29+
30+
public class A
31+
{
32+
private int _id = 5;
33+
private string _name =string.Empty;
34+
35+
public int Id => _id;
36+
37+
public string Name => _name;
38+
}
39+
}
40+
}

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+
}

src/NHibernate/Bytecode/IBytecodeProvider.cs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using NHibernate.Bytecode.Lightweight;
23
using NHibernate.Properties;
34

45
namespace NHibernate.Bytecode
@@ -19,6 +20,8 @@ public interface IBytecodeProvider
1920
/// <param name="getters">All property getters to be accessed via reflection.</param>
2021
/// <param name="setters">All property setters to be accessed via reflection.</param>
2122
/// <returns>The reflection optimization delegate.</returns>
23+
// Since 5.3
24+
[Obsolete("Please use NHibernate.Bytecode.BytecodeProviderExtensions.GetReflectionOptimizer instead")]
2225
IReflectionOptimizer GetReflectionOptimizer(System.Type clazz, IGetter[] getters, ISetter[] setters);
2326

2427
/// <summary>
@@ -48,4 +51,32 @@ public interface IBytecodeProvider
4851
// Not ported
4952
//ClassTransformer getTransformer(ClassFilter classFilter, FieldFilter fieldFilter);
5053
}
51-
}
54+
55+
public static class BytecodeProviderExtensions
56+
{
57+
/// <summary>
58+
/// Retrieve the <see cref="IReflectionOptimizer" /> delegate for this provider
59+
/// capable of generating reflection optimization components.
60+
/// </summary>
61+
/// <param name="bytecodeProvider">The bytecode provider.</param>
62+
/// <param name="clazz">The class to be reflected upon.</param>
63+
/// <param name="getters">All property getters to be accessed via reflection.</param>
64+
/// <param name="setters">All property setters to be accessed via reflection.</param>
65+
/// <param name="specializedGetter">The specialized getter for the given type.</param>
66+
/// <param name="specializedSetter">The specialized setter for the given type.</param>
67+
/// <returns>The reflection optimization delegate.</returns>
68+
//6.0 TODO: Merge into IBytecodeProvider.
69+
public static IReflectionOptimizer GetReflectionOptimizer(this IBytecodeProvider bytecodeProvider, System.Type clazz, IGetter[] getters,
70+
ISetter[] setters, IGetter specializedGetter, ISetter specializedSetter)
71+
{
72+
if (bytecodeProvider is BytecodeProviderImpl bytecodeProviderImpl)
73+
{
74+
return bytecodeProviderImpl.GetReflectionOptimizer(clazz, getters, setters, specializedGetter, specializedSetter);
75+
}
76+
77+
#pragma warning disable 618
78+
return bytecodeProvider.GetReflectionOptimizer(clazz, getters, setters);
79+
#pragma warning restore 618
80+
}
81+
}
82+
}

0 commit comments

Comments
 (0)