Skip to content

Extend IAccessOptimizer to support getting/setting single property value #1944

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jan 1, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/NHibernate.Test/NHSpecificTest/NH3119/FixtureByCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ public class ComponentTestReflectionOptimizer : ReflectionOptimizer
public static bool IsCalledForComponent { get; set; }

public ComponentTestReflectionOptimizer(System.Type mappedType, IGetter[] getters, ISetter[] setters) :
base(mappedType, getters, setters)
base(mappedType, getters, setters, null, null)
{
_logCall = mappedType == typeof(Component);
}
Expand Down
135 changes: 135 additions & 0 deletions src/NHibernate.Test/PropertyTest/AccessorPerformanceFixture.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using NHibernate.Bytecode;
using NHibernate.Bytecode.Lightweight;
using NHibernate.Properties;
using NUnit.Framework;

namespace NHibernate.Test.PropertyTest
{
public abstract class AccessorPerformanceFixture<T> where T : new()
{
private IPropertyAccessor _accessor;
private IAccessOptimizer _optimizer;
private ISetter[] _setters;
private IGetter[] _getters;

protected abstract string AccessorType { get; }

protected abstract List<string> PropertyNames { get; }

protected abstract object GetValue(int i);

[SetUp]
public void SetUp()
{
_accessor = PropertyAccessorFactory.GetPropertyAccessor(AccessorType);

_getters = new IGetter[PropertyNames.Count];
_setters = new ISetter[PropertyNames.Count];
var type = typeof(T);

for (var i = 0; i < PropertyNames.Count; i++)
{
_getters[i] = _accessor.GetGetter(type, PropertyNames[i]);
_setters[i] = _accessor.GetSetter(type, PropertyNames[i]);
}

_optimizer = new ReflectionOptimizer(type, _getters, _setters, null, null).AccessOptimizer;
}

[TestCase(50000)]
[TestCase(100000)]
[TestCase(200000)]
[TestCase(500000)]
public void TestGetPropertyValue(int iter)
{
var target = new T();
var stopwatch = new Stopwatch();

// Warm up
TestGetter(target, 100);
TestOptimizedGetter(target, 100);

stopwatch.Restart();
TestGetter(target, iter);
stopwatch.Stop();
Console.WriteLine($"Reflection getter total time for {iter} iterations: {stopwatch.ElapsedMilliseconds}ms");

stopwatch.Restart();
TestOptimizedGetter(target, iter);
stopwatch.Stop();
Console.WriteLine($"IL getter total time for {iter} iterations: {stopwatch.ElapsedMilliseconds}ms");
}

[TestCase(50000)]
[TestCase(100000)]
[TestCase(200000)]
[TestCase(500000)]
public void TestSetPropertyValue(int iter)
{
var target = new T();
var stopwatch = new Stopwatch();

// Warm up
TestSetter(target, 100);
TestOptimizedSetter(target, 100);

stopwatch.Restart();
TestSetter(target, iter);
stopwatch.Stop();
Console.WriteLine($"Reflection setter total time for {iter} iterations: {stopwatch.ElapsedMilliseconds}ms");

stopwatch.Restart();
TestOptimizedSetter(target, iter);
stopwatch.Stop();
Console.WriteLine($"IL setter total time for {iter} iterations: {stopwatch.ElapsedMilliseconds}ms");
}

private void TestGetter(object target, int iter)
{
for (var i = 0; i < iter; i++)
{
for (var j = 0; j < _getters.Length; j++)
{
var val = _getters[j].Get(target);
}
}
}

private void TestOptimizedGetter(object target, int iter)
{
for (var i = 0; i < iter; i++)
{
for (var j = 0; j < _getters.Length; j++)
{
var val = _optimizer.GetPropertyValue(target, j);
}
}
}

private void TestSetter(object target, int iter)
{
for (var i = 0; i < iter; i++)
{
for (var j = 0; j < _setters.Length; j++)
{
_setters[j].Set(target, GetValue(j));
}
}
}

private void TestOptimizedSetter(object target, int iter)
{
for (var i = 0; i < iter; i++)
{
for (var j = 0; j < _setters.Length; j++)
{
_optimizer.SetPropertyValue(target, j, GetValue(j));
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System.Collections.Generic;
using NUnit.Framework;

namespace NHibernate.Test.PropertyTest
{
[TestFixture, Explicit]
public class BasicPropertyAccessorPerformanceFixture : AccessorPerformanceFixture<BasicPropertyAccessorPerformanceFixture.A>
{
protected override string AccessorType => "property";

protected override List<string> PropertyNames => new List<string>
{
nameof(A.Id),
nameof(A.Name)
};

protected override object GetValue(int i)
{
switch (i)
{
case 0:
return 5;
case 1:
return "name";
}

return null;
}

public class A
{
public int Id { get; set; }

public string Name { get; set; }
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System.Collections.Generic;
using NUnit.Framework;

namespace NHibernate.Test.PropertyTest
{
[TestFixture, Explicit]
public class FieldAccessorPerformanceFixture : AccessorPerformanceFixture<FieldAccessorPerformanceFixture.A>
{
protected override string AccessorType => "field";

protected override List<string> PropertyNames => new List<string>
{
"_id",
"_name"
};

protected override object GetValue(int i)
{
switch (i)
{
case 0:
return 5;
case 1:
return "name";
}

return null;
}

public class A
{
private int _id = 5;
private string _name =string.Empty;

public int Id => _id;

public string Name => _name;
}
}
}
8 changes: 5 additions & 3 deletions src/NHibernate.Test/ReflectionOptimizerTest/LcgFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public void NoSetter()
new BasicPropertyAccessor.BasicSetter(typeof (NoSetterClass), typeof (NoSetterClass).GetProperty("Property"), "Property")
};

Assert.Throws<PropertyNotFoundException>(() => new ReflectionOptimizer(typeof(NoSetterClass), getters, setters));
Assert.Throws<PropertyNotFoundException>(() => new ReflectionOptimizer(typeof(NoSetterClass), getters, setters, null, null));
}

public class NoGetterClass
Expand All @@ -52,7 +52,7 @@ public void NoGetter()
new BasicPropertyAccessor.BasicSetter(typeof (NoGetterClass), typeof (NoGetterClass).GetProperty("Property"), "Property")
};

Assert.Throws<PropertyNotFoundException>(() => new ReflectionOptimizer(typeof (NoGetterClass), getters, setters));
Assert.Throws<PropertyNotFoundException>(() => new ReflectionOptimizer(typeof (NoGetterClass), getters, setters, null, null));
}

public class GetterTypeMismatchClass
Expand Down Expand Up @@ -83,7 +83,9 @@ public void TestGetterTypeMismatch()
ReflectionOptimizer reflectionOptimizer = new ReflectionOptimizer(
obj.GetType(),
new[] { accessor.GetGetter(obj.GetType(), property) },
new[] { accessor.GetSetter(obj.GetType(), property) });
new[] { accessor.GetSetter(obj.GetType(), property) },
null,
null);

IAccessOptimizer accessOptimizer = reflectionOptimizer.AccessOptimizer;

Expand Down
66 changes: 65 additions & 1 deletion src/NHibernate/Bytecode/IAccessOptimizer.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
using System;
using NHibernate.Bytecode.Lightweight;

namespace NHibernate.Bytecode
{
/// <summary>
Expand All @@ -8,4 +11,65 @@ public interface IAccessOptimizer
object[] GetPropertyValues(object target);
void SetPropertyValues(object target, object[] values);
}
}

public static class AccessOptimizerExtensions
{
/// <summary>
/// Get the property value on the given index.
/// </summary>
//6.0 TODO: Merge into IAccessOptimizer.
public static object GetPropertyValue(this IAccessOptimizer optimizer, object target, int i)
{
if (optimizer is AccessOptimizer accessOptimizer)
{
return accessOptimizer.GetPropertyValue(target, i);
}

throw new InvalidOperationException($"{optimizer.GetType()} does not support {nameof(GetPropertyValue)} method.");
}

/// <summary>
/// Set the property value on the given index.
/// </summary>
//6.0 TODO: Merge into IAccessOptimizer.
public static void SetPropertyValue(this IAccessOptimizer optimizer, object target, int i, object value)
{
if (optimizer is AccessOptimizer accessOptimizer)
{
accessOptimizer.SetPropertyValue(target, i, value);
return;
}

throw new InvalidOperationException($"{optimizer.GetType()} does not support {nameof(SetPropertyValue)} method.");
}

/// <summary>
/// Get the specialized property value.
/// </summary>
//6.0 TODO: Merge into IAccessOptimizer.
internal static object GetSpecializedPropertyValue(this IAccessOptimizer optimizer, object target)
{
if (optimizer is AccessOptimizer accessOptimizer)
{
return accessOptimizer.GetSpecializedPropertyValue(target);
}

throw new InvalidOperationException($"{optimizer.GetType()} does not support {nameof(GetPropertyValue)} method.");
}

/// <summary>
/// Set the specialized property value.
/// </summary>
//6.0 TODO: Merge into IAccessOptimizer.
internal static void SetSpecializedPropertyValue(this IAccessOptimizer optimizer, object target, object value)
{
if (optimizer is AccessOptimizer accessOptimizer)
{
accessOptimizer.SetSpecializedPropertyValue(target, value);
return;
}

throw new InvalidOperationException($"{optimizer.GetType()} does not support {nameof(SetPropertyValue)} method.");
}
}
}
33 changes: 32 additions & 1 deletion src/NHibernate/Bytecode/IBytecodeProvider.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using NHibernate.Bytecode.Lightweight;
using NHibernate.Properties;

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

/// <summary>
Expand Down Expand Up @@ -48,4 +51,32 @@ public interface IBytecodeProvider
// Not ported
//ClassTransformer getTransformer(ClassFilter classFilter, FieldFilter fieldFilter);
}
}

public static class BytecodeProviderExtensions
{
/// <summary>
/// Retrieve the <see cref="IReflectionOptimizer" /> delegate for this provider
/// capable of generating reflection optimization components.
/// </summary>
/// <param name="bytecodeProvider">The bytecode provider.</param>
/// <param name="clazz">The class to be reflected upon.</param>
/// <param name="getters">All property getters to be accessed via reflection.</param>
/// <param name="setters">All property setters to be accessed via reflection.</param>
/// <param name="specializedGetter">The specialized getter for the given type.</param>
/// <param name="specializedSetter">The specialized setter for the given type.</param>
/// <returns>The reflection optimization delegate.</returns>
//6.0 TODO: Merge into IBytecodeProvider.
public static IReflectionOptimizer GetReflectionOptimizer(this IBytecodeProvider bytecodeProvider, System.Type clazz, IGetter[] getters,
ISetter[] setters, IGetter specializedGetter, ISetter specializedSetter)
{
if (bytecodeProvider is BytecodeProviderImpl bytecodeProviderImpl)
{
return bytecodeProviderImpl.GetReflectionOptimizer(clazz, getters, setters, specializedGetter, specializedSetter);
}

#pragma warning disable 618
return bytecodeProvider.GetReflectionOptimizer(clazz, getters, setters);
#pragma warning restore 618
}
}
}
Loading