Skip to content

Commit dca47d2

Browse files
committed
Fixed the IL generation of the get/set methods
1 parent 4edb066 commit dca47d2

File tree

11 files changed

+82
-100
lines changed

11 files changed

+82
-100
lines changed

src/NHibernate.Test/PropertyTest/AccessorPerformanceFixture.cs

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ namespace NHibernate.Test.PropertyTest
2020

2121
protected abstract List<string> PropertyNames { get; }
2222

23-
protected abstract object GetValue(int i);
23+
protected abstract object[] GetValues();
2424

2525
[SetUp]
2626
public void SetUp()
@@ -44,48 +44,60 @@ public void SetUp()
4444
[TestCase(100000)]
4545
[TestCase(200000)]
4646
[TestCase(500000)]
47-
public void TestGetPropertyValue(int iter)
47+
public void TestGetter(int iter)
4848
{
4949
var target = new T();
5050
var stopwatch = new Stopwatch();
5151

5252
// Warm up
5353
TestGetter(target, 100);
5454
TestOptimizedGetter(target, 100);
55+
TestOptimizedMultiGetter(target, 100);
5556

5657
stopwatch.Restart();
5758
TestGetter(target, iter);
5859
stopwatch.Stop();
59-
Console.WriteLine($"Reflection getter total time for {iter} iterations: {stopwatch.ElapsedMilliseconds}ms");
60+
Console.WriteLine($"IGetter.Get total time for {iter} iterations: {stopwatch.ElapsedMilliseconds}ms");
6061

6162
stopwatch.Restart();
6263
TestOptimizedGetter(target, iter);
6364
stopwatch.Stop();
64-
Console.WriteLine($"IL getter total time for {iter} iterations: {stopwatch.ElapsedMilliseconds}ms");
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");
6571
}
6672

6773
[TestCase(50000)]
6874
[TestCase(100000)]
6975
[TestCase(200000)]
7076
[TestCase(500000)]
71-
public void TestSetPropertyValue(int iter)
77+
public void TestSetter(int iter)
7278
{
7379
var target = new T();
7480
var stopwatch = new Stopwatch();
7581

7682
// Warm up
7783
TestSetter(target, 100);
7884
TestOptimizedSetter(target, 100);
85+
TestOptimizedMultiSetter(target, 100);
7986

8087
stopwatch.Restart();
8188
TestSetter(target, iter);
8289
stopwatch.Stop();
83-
Console.WriteLine($"Reflection setter total time for {iter} iterations: {stopwatch.ElapsedMilliseconds}ms");
90+
Console.WriteLine($"ISetter.Set total time for {iter} iterations: {stopwatch.ElapsedMilliseconds}ms");
8491

8592
stopwatch.Restart();
8693
TestOptimizedSetter(target, iter);
8794
stopwatch.Stop();
88-
Console.WriteLine($"IL setter total time for {iter} iterations: {stopwatch.ElapsedMilliseconds}ms");
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");
89101
}
90102

91103
private void TestGetter(object target, int iter)
@@ -110,13 +122,22 @@ private void TestOptimizedGetter(object target, int iter)
110122
}
111123
}
112124

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+
113133
private void TestSetter(object target, int iter)
114134
{
115135
for (var i = 0; i < iter; i++)
116136
{
137+
var values = GetValues();
117138
for (var j = 0; j < _setters.Length; j++)
118139
{
119-
_setters[j].Set(target, GetValue(j));
140+
_setters[j].Set(target, values[j]);
120141
}
121142
}
122143
}
@@ -125,11 +146,20 @@ private void TestOptimizedSetter(object target, int iter)
125146
{
126147
for (var i = 0; i < iter; i++)
127148
{
149+
var values = GetValues();
128150
for (var j = 0; j < _setters.Length; j++)
129151
{
130-
_optimizer.SetPropertyValue(target, j, GetValue(j));
152+
_optimizer.SetPropertyValue(target, j, values[j]);
131153
}
132154
}
133155
}
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+
}
134164
}
135165
}
Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Collections.Generic;
1+
using System;
2+
using System.Collections.Generic;
23
using NUnit.Framework;
34

45
namespace NHibernate.Test.PropertyTest
@@ -11,27 +12,25 @@ public class BasicPropertyAccessorPerformanceFixture : AccessorPerformanceFixtur
1112
protected override List<string> PropertyNames => new List<string>
1213
{
1314
nameof(A.Id),
14-
nameof(A.Name)
15+
nameof(A.Name),
16+
nameof(A.Date),
17+
nameof(A.Decimal)
1518
};
1619

17-
protected override object GetValue(int i)
20+
protected override object[] GetValues()
1821
{
19-
switch (i)
20-
{
21-
case 0:
22-
return 5;
23-
case 1:
24-
return "name";
25-
}
26-
27-
return null;
22+
return new object[] { 5, "name", DateTime.MaxValue, 1.5m };
2823
}
2924

3025
public class A
3126
{
3227
public int Id { get; set; }
3328

3429
public string Name { get; set; }
30+
31+
public DateTime Date { get; set; }
32+
33+
public decimal? Decimal { get; set; }
3534
}
3635
}
3736
}
Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Collections.Generic;
1+
using System;
2+
using System.Collections.Generic;
23
using NUnit.Framework;
34

45
namespace NHibernate.Test.PropertyTest
@@ -11,30 +12,30 @@ public class FieldAccessorPerformanceFixture : AccessorPerformanceFixture<FieldA
1112
protected override List<string> PropertyNames => new List<string>
1213
{
1314
"_id",
14-
"_name"
15+
"_name",
16+
"_date",
17+
"_decimal",
1518
};
1619

17-
protected override object GetValue(int i)
20+
protected override object[] GetValues()
1821
{
19-
switch (i)
20-
{
21-
case 0:
22-
return 5;
23-
case 1:
24-
return "name";
25-
}
26-
27-
return null;
22+
return new object[] {5, "name", DateTime.MaxValue, 1.5m};
2823
}
2924

3025
public class A
3126
{
3227
private int _id = 5;
3328
private string _name =string.Empty;
29+
private DateTime _date = DateTime.MinValue;
30+
private decimal? _decimal = decimal.Zero;
3431

3532
public int Id => _id;
3633

3734
public string Name => _name;
35+
36+
public DateTime Date => _date;
37+
38+
public decimal? Decimal => _decimal;
3839
}
3940
}
4041
}

src/NHibernate/Bytecode/IBytecodeProvider.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,9 @@ public static class BytecodeProviderExtensions
6666
/// <param name="specializedSetter">The specialized setter for the given type.</param>
6767
/// <returns>The reflection optimization delegate.</returns>
6868
//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)
69+
public static IReflectionOptimizer GetReflectionOptimizer(
70+
this IBytecodeProvider bytecodeProvider, System.Type clazz, IGetter[] getters, ISetter[] setters,
71+
IGetter specializedGetter, ISetter specializedSetter)
7172
{
7273
if (bytecodeProvider is BytecodeProviderImpl bytecodeProviderImpl)
7374
{

src/NHibernate/Bytecode/Lightweight/AccessOptimizer.cs

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -95,15 +95,7 @@ private static void SetPropertyValue(object target, object value, Setter setter)
9595
}
9696
else
9797
{
98-
// 6.0 TODO: remove the try/catch block once CanEmit will be part of the IOptimizableSetter
99-
try
100-
{
101-
setter.Optimized(target, value);
102-
}
103-
catch (VerificationException) // Will occur for readonly fields and will heavily impact on performance when occurred.
104-
{
105-
setter.Default.Set(target, value);
106-
}
98+
setter.Optimized(target, value);
10799
}
108100
}
109101
}

src/NHibernate/Bytecode/Lightweight/ReflectionOptimizer.cs

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,9 @@ public ReflectionOptimizer(System.Type mappedType, IGetter[] getters, ISetter[]
7373
setInvoker,
7474
getMethods,
7575
setMethods,
76-
new Getter(
77-
specializedGetter,
78-
specializedGetter != null ? GenerateGetPropertyValueMethod(specializedGetter) : null),
79-
new Setter(
80-
specializedSetter,
81-
specializedSetter != null ? GenerateSetPropertyValueMethod(specializedSetter) : null));
76+
new Getter(specializedGetter, GenerateGetPropertyValueMethod(specializedGetter)),
77+
new Setter(specializedSetter, GenerateSetPropertyValueMethod(specializedSetter))
78+
);
8279

8380
createInstanceMethod = CreateCreateInstanceMethod(mappedType);
8481
}
@@ -172,7 +169,7 @@ private Func<object, object> GenerateGetPropertyValueMethod(IGetter getter)
172169
return null;
173170
}
174171

175-
var method = new DynamicMethod(string.Empty, typeof(object), new[] { typeof(object) }, CanSkipVisibilityChecks());
172+
var method = CreateDynamicMethod(typeof(object), new[] { typeof(object) });
176173
var il = method.GetILGenerator();
177174

178175
// object (target) { (object) ((ClassType) target).GetMethod(); }
@@ -187,18 +184,18 @@ private Func<object, object> GenerateGetPropertyValueMethod(IGetter getter)
187184

188185
private Action<object, object> GenerateSetPropertyValueMethod(ISetter setter)
189186
{
190-
if (!(setter is IOptimizableSetter optimizableSetter) || !optimizableSetter.CanEmit())
187+
if (!(setter is IOptimizableSetter optimizableSetter))
191188
{
192189
return null;
193190
}
194191

195192
// void (target, value) { ((ClassType) target).SetMethod((FieldType) value); }
196-
var method = new DynamicMethod(string.Empty, null, new[] { typeof(object), typeof(object) }, CanSkipVisibilityChecks());
193+
var method = CreateDynamicMethod(null, new[] { typeof(object), typeof(object) });
197194
var il = method.GetILGenerator();
198195
il.Emit(OpCodes.Ldarg_0);
199196
EmitCastToReference(il, mappedType);
200197
il.Emit(OpCodes.Ldarg_1);
201-
il.Emit(optimizableSetter.Type.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, optimizableSetter.Type);
198+
EmitUtil.PreparePropertyForSet(il, optimizableSetter.Type);
202199
optimizableSetter.Emit(il);
203200
il.Emit(OpCodes.Ret);
204201

src/NHibernate/Properties/FieldAccessor.cs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -331,11 +331,6 @@ public void Emit(ILGenerator il)
331331
{
332332
il.Emit(OpCodes.Stfld, field);
333333
}
334-
335-
internal bool CanEmit()
336-
{
337-
return !field.IsInitOnly;
338-
}
339334
}
340335
}
341336
}

src/NHibernate/Properties/IOptimizableSetter.cs

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,22 +21,4 @@ public interface IOptimizableSetter
2121
/// </summary>
2222
void Emit(ILGenerator il);
2323
}
24-
25-
internal static class OptimizableSetterExtensions
26-
{
27-
/// <summary>
28-
/// Determine if the optimizable setter can actually be optimized.
29-
/// </summary>
30-
/// <returns><see langword="true" /> if can be optimized, <see langword="false" /> otherwise.</returns>
31-
//6.0 TODO: Merge into IOptimizableSetter.
32-
public static bool CanEmit(this IOptimizableSetter setter)
33-
{
34-
if (setter is FieldAccessor.FieldSetter fieldSetter)
35-
{
36-
return fieldSetter.CanEmit();
37-
}
38-
39-
return true;
40-
}
41-
}
42-
}
24+
}

src/NHibernate/Tuple/Component/PocoComponentTuplizer.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public class PocoComponentTuplizer : AbstractComponentTuplizer
1919
private readonly IGetter parentGetter;
2020
[NonSerialized]
2121
private IReflectionOptimizer optimizer;
22-
private readonly bool isBytecodeProviderImpl; // 6.0 TODO: remove
22+
private bool isBytecodeProviderImpl; // 6.0 TODO: remove
2323

2424

2525
[OnDeserialized]
@@ -54,7 +54,6 @@ public PocoComponentTuplizer(Mapping.Component component)
5454
parentGetter = parentProperty.GetGetter(componentClass);
5555
}
5656

57-
isBytecodeProviderImpl = Cfg.Environment.BytecodeProvider is BytecodeProviderImpl;
5857
SetReflectionOptimizer();
5958

6059
// Fix for NH-3119
@@ -169,6 +168,7 @@ protected void SetReflectionOptimizer()
169168
if (Cfg.Environment.UseReflectionOptimizer)
170169
{
171170
optimizer = Cfg.Environment.BytecodeProvider.GetReflectionOptimizer(componentClass, getters, setters, parentGetter, parentSetter);
171+
isBytecodeProviderImpl = Cfg.Environment.BytecodeProvider is BytecodeProviderImpl;
172172
}
173173
}
174174

src/NHibernate/Tuple/Entity/AbstractEntityTuplizer.cs

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -183,17 +183,7 @@ public object GetVersion(object entity)
183183
return GetPropertyValue(entity, entityMetamodel.VersionPropertyIndex);
184184
}
185185

186-
// 6.0 TODO: make it virtual
187-
public void SetPropertyValue(object entity, int i, object value)
188-
{
189-
#pragma warning disable 618
190-
SetValue(entity, i, value);
191-
#pragma warning restore 618
192-
}
193-
194-
// Since 5.3
195-
[Obsolete("Use SetPropertyValue instead.")]
196-
protected virtual void SetValue(object entity, int i, object value)
186+
public virtual void SetPropertyValue(object entity, int i, object value)
197187
{
198188
setters[i].Set(entity, value);
199189
}

0 commit comments

Comments
 (0)