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 all commits
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
165 changes: 165 additions & 0 deletions src/NHibernate.Test/PropertyTest/AccessorPerformanceFixture.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
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[] GetValues();

[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 TestGetter(int iter)
{
var target = new T();
var stopwatch = new Stopwatch();

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

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

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

stopwatch.Restart();
TestOptimizedMultiGetter(target, iter);
stopwatch.Stop();
Console.WriteLine($"IAccessOptimizer.GetPropertyValues total time for {iter} iterations: {stopwatch.ElapsedMilliseconds}ms");
}

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

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

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

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

stopwatch.Restart();
TestOptimizedMultiSetter(target, iter);
stopwatch.Stop();
Console.WriteLine($"IAccessOptimizer.SetPropertyValues 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 TestOptimizedMultiGetter(object target, int iter)
{
for (var i = 0; i < iter; i++)
{
var val = _optimizer.GetPropertyValues(target);
}
}

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

private void TestOptimizedSetter(object target, int iter)
{
for (var i = 0; i < iter; i++)
{
var values = GetValues();
for (var j = 0; j < _setters.Length; j++)
{
_optimizer.SetPropertyValue(target, j, values[j]);
}
}
}

private void TestOptimizedMultiSetter(object target, int iter)
{
for (var i = 0; i < iter; i++)
{
_optimizer.SetPropertyValues(target, GetValues());
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System;
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),
nameof(A.Date),
nameof(A.Decimal)
};

protected override object[] GetValues()
{
return new object[] { 5, "name", DateTime.MaxValue, 1.5m };
}

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

public string Name { get; set; }

public DateTime Date { get; set; }

public decimal? Decimal { get; set; }
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System;
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",
"_date",
"_decimal",
};

protected override object[] GetValues()
{
return new object[] {5, "name", DateTime.MaxValue, 1.5m};
}

public class A
{
private int _id = 5;
private string _name =string.Empty;
private DateTime _date = DateTime.MinValue;
private decimal? _decimal = decimal.Zero;

public int Id => _id;

public string Name => _name;

public DateTime Date => _date;

public decimal? Decimal => _decimal;
}
}
}
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.");
}
}
}
Loading