Reflection, кэширование результатов
От: HowardLovekraft  
Дата: 07.12.10 13:31
Оценка:
Здравствуйте.
Нужно постоянно читать/устанавливать значение свойства объекта через reflection.
Имеет ли смысл сделать кэш метаданных для увеличения скорости, вроде такого:
class MyClass
{
  private Object _target;
  private PropertyInfo _targetProperty;

  public MyClass(Object target)
  {
    _target = target;
    _targetProperty = _target.GetType().GetProperty(...);
  }

  public void Foo()
  {
    _targetProperty.SetValue(_target, ...);
  }
}

или все уже придумано до нас и можно со спокойной совестью делать так:
class MyClass
{
  private Object _target;

  public MyClass(Object target)
  {
    _target = target;
  }

  public void Foo()
  {
    _target.GetType().GetProperty(...).SetValue(_target, ...);
  }
}

?
FW4, если это имеет значение. Желательно с пруфлинком.
Re: Reflection, кэширование результатов
От: _FRED_ Черногория
Дата: 07.12.10 13:51
Оценка:
Здравствуйте, HowardLovekraft, Вы писали:

HL>Нужно постоянно читать/устанавливать значение свойства объекта через reflection.

HL>Имеет ли смысл сделать кэш метаданных для увеличения скорости, вроде такого:

Если речь об оптимизациях, то надо сказать о целях оптимизации. Сделайте без кеша — удовлетворит или нет? Сделайте с кешем. Сравните.

Для примера: дефолтовая реализация TypeDescriptionProvider, работающая через рефлекшен, кэширует у себя некоторые результаты и в большинстве случаев этого более чем достаточно. Но в некоторых операциях имеет смысл самому закешировать результат, что бы съэкономить на обращениях к тому внутреннему кешу :о)

Точно так же и тут: посмотрите, удовлетворит ли вас производительность без присиданий и, если нет, то удовлетворят ли приседания. А то я вам скажу — нет, не делайте, там внутри конечно всё в некотором роде закешироано да и работает ("в моих кейсах") достаточно быстро. А вы попробуете у себя и скажите "ан нет, врёшь: если закешировать, у меня втрое быстрее получается". А у меня может хоть в десять раз медленнее, всё равно без микроскопа не разглядеть?
Help will always be given at Hogwarts to those who ask for it.
Re[2]: Reflection, кэширование результатов
От: HowardLovekraft  
Дата: 07.12.10 14:00
Оценка:
Здравствуйте, _FRED_, Вы писали:

Скорее, вопрос про существование кэша в недрах рантайма — в принципе он есть или нет, и если есть, то что там кэшируется и при каких условиях. Интересуют именно результаты Object.GetType, Type.GetProperty, Type.GetMethod.

Я понимаю, что итоговая реализация будет сильно зависеть от вариантов использования и, возможно, свой кэш не понадобится.
Re: Reflection, кэширование результатов
От: Sinix  
Дата: 07.12.10 14:21
Оценка: 4 (1) +1
Здравствуйте, HowardLovekraft, Вы писали:

HL>Здравствуйте.

HL>Нужно постоянно читать/устанавливать значение свойства объекта через reflection.
HL>Имеет ли смысл сделать кэш метаданных для увеличения скорости, вроде такого:

Имеет — я специально для такого варианта писал примитивный враппер и хранил в нём делегаты на геттер и сеттер (созданные через Delegate.Create (или как там его)).
Какие грабли я таким образом решал, точно не помню, но производительность от него точно не ухудшилась. Если интересует, завтра могу скинуть код.

В теории скорость должна приблизиться к emit-у, если нет, можно сгенерить код самому, благо System.Linq.Expressions сделали это дело до неприличия простым.
Re: Reflection, кэширование результатов
От: k.o. Россия  
Дата: 07.12.10 14:29
Оценка: 4 (1)
Здравствуйте, HowardLovekraft, Вы писали:

HL>Здравствуйте.

HL>Нужно постоянно читать/устанавливать значение свойства объекта через reflection.
HL>Имеет ли смысл сделать кэш метаданных для увеличения скорости, вроде такого:
HL>?
HL>FW4, если это имеет значение. Желательно с пруфлинком.

Имеет (в том числе для FW4), при частом чтении/записи разница заметна невооружённым взглядом. Сам недано с таким сталкивался. Код не дам (NDA), но тесты должны быть тривиальны.

Ещё, при большом желании, можно вобще избавиться от Reflection'а и генерировать код чтения/записи, например, через Linq.Expressions, разница в скорости тоже достигает нескольких раз.
Re[2]: Reflection, кэширование результатов
От: HowardLovekraft  
Дата: 07.12.10 14:37
Оценка:
Здравствуйте, k.o., Вы писали:

KO>при большом желании, можно вобще избавиться от Reflection'а и генерировать код чтения/записи, например, через Linq.Expressions

Ткните, пожалуйста, в место, где об этом можно почитать (генерация код чтения/записи через expressions).
Re[2]: Reflection, кэширование результатов
От: HowardLovekraft  
Дата: 07.12.10 14:37
Оценка:
Здравствуйте, Sinix, Вы писали:

S>Если интересует, завтра могу скинуть код.

Спасибо. Конечно, интересует.
Re[3]: Reflection, кэширование результатов
От: k.o. Россия  
Дата: 07.12.10 15:01
Оценка: 4 (1)
Здравствуйте, HowardLovekraft, Вы писали:

HL>Здравствуйте, k.o., Вы писали:


KO>>при большом желании, можно вобще избавиться от Reflection'а и генерировать код чтения/записи, например, через Linq.Expressions

HL>Ткните, пожалуйста, в место, где об этом можно почитать (генерация код чтения/записи через expressions).

здесь оно же элементарно, держи:

using System;
using System.Linq.Expressions;
using System.Reflection;

class Foo
{
    public int Data
    {
        get;
        set;
    }
}

static class Program
{
    static Func<object, object> MakeGetter(PropertyInfo info)
    {
        var objectParameter = Expression.Parameter(typeof(object));
        var getterExpression = Expression.Lambda<Func<object, object>>(
            Expression.Convert(
                Expression.Property(
                    Expression.Convert(objectParameter, info.DeclaringType),
                    info),
                typeof(object)),
                objectParameter);

        return getterExpression.Compile();
    }

    static Action<object, object> MakeSetter(PropertyInfo info)
    {
        var objectParameter = Expression.Parameter(typeof(object));
        var valueParameter = Expression.Parameter(typeof(object));
        var setterExpression = Expression.Lambda<Action<object, object>>(
            Expression.Assign(
                Expression.Property(
                    Expression.Convert(objectParameter, info.DeclaringType),
                    info),
                Expression.Convert(valueParameter, info.PropertyType)),
            objectParameter,
            valueParameter);

        return setterExpression.Compile();
    }

    static void Main()
    {
        var foo = new Foo();
        var property = typeof(Foo).GetProperty("Data");
        var getter = MakeGetter(property);
        foo.Data = 10;
        Console.WriteLine(getter(foo));

        var setter = MakeSetter(property);
        setter(foo, 0);
        Console.WriteLine(foo.Data);
    }
}
Re[3]: Reflection, кэширование результатов
От: Sinix  
Дата: 07.12.10 15:29
Оценка:
Здравствуйте, HowardLovekraft, Вы писали:

HL>Спасибо. Конечно, интересует.

Ок.
Re[2]: Reflection, кэширование результатов
От: VladD2 Российская Империя www.nemerle.org
Дата: 07.12.10 19:32
Оценка:
Здравствуйте, k.o., Вы писали:

KO>Ещё, при большом желании, можно вобще избавиться от Reflection'а и генерировать код чтения/записи, например, через Linq.Expressions, разница в скорости тоже достигает нескольких раз.


Не надо тут никакого Linq.Expressions. Это как из пушки по воробьям. Для чтения свойств и вызова методов отлично подходят делегаты. Sinix прав
Автор: Sinix
Дата: 07.12.10
. Через рефлксию получаете проперти-инфо, через них метод-инфо, и Delegate.Create() получаете ссылку на делегат. Вот ее и прикапывайте. Скорость будет чуть хуже чем у виртуального вызова, т.е. ахринительная. Быстрее не добиться. Вот пример кода:
using System;
using System.Text;
using System.Reflection;

class Program
{
  static void Main(string[] args)
  {
    const string methodName = "Length";

    var a = new StringBuilder("test");
    object obj = a;

    var getter = MakeGetterDelegate<int>(obj, methodName);
    var setter = MakeSetterDelegate<int>(obj, methodName);

    Console.WriteLine(getter());
    setter(5);
    Console.WriteLine(a.Length);
    Console.WriteLine("'" + a + "'");
  }

  static Action<T> MakeSetterDelegate<T>(object obj, string propertyName)
  {
    var setter = obj.GetType().GetProperty(propertyName).GetSetMethod(true);
    var result = Delegate.CreateDelegate(typeof(Action<T>), obj, setter);
    return (Action<T>)result;
  }

  static Func<T> MakeGetterDelegate<T>(object obj, string propertyName)
  {
    var getter = obj.GetType().GetProperty(propertyName).GetGetMethod(true);
    var result = Delegate.CreateDelegate(typeof(Func<T>), obj, getter);
    return (Func<T>)result;
  }
}

выводит:
4
5
'test '
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: Reflection, кэширование результатов
От: k.o. Россия  
Дата: 07.12.10 22:02
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Здравствуйте, k.o., Вы писали:


KO>>Ещё, при большом желании, можно вобще избавиться от Reflection'а и генерировать код чтения/записи, например, через Linq.Expressions, разница в скорости тоже достигает нескольких раз.


VD>Не надо тут никакого Linq.Expressions. Это как из пушки по воробьям.


Во-первых см. выделенное.

VD>Для чтения свойств и вызова методов отлично подходят делегаты. Sinix прав
Автор: Sinix
Дата: 07.12.10
. Через рефлксию получаете проперти-инфо, через них метод-инфо, и Delegate.Create() получаете ссылку на делегат. Вот ее и прикапывайте. Скорость будет чуть хуже чем у виртуального вызова, т.е. ахринительная. Быстрее не добиться. Вот пример кода:


Во-вторых, твой код и мой не эквивалентны. Моему не нужно знать тип свойства на этапе компиляции и он может работать с разными объектами, зато твой должен быть быстрее. Нужен-ли кому-нибудь такой функционал? Мне, например, ни Linq.Expression ни этих делегатов не понадобилось — хватило скорости PropertyInfo.GetValue/SetValue, а ТС пусть сам решает, что ему нужно.
Re[3]: Reflection, кэширование результатов
От: Sinix  
Дата: 08.12.10 02:05
Оценка: 4 (1)
Здравствуйте, HowardLovekraft, Вы писали:

HL>Здравствуйте, Sinix, Вы писали:


S>>Если интересует, завтра могу скинуть код.

HL>Спасибо. Конечно, интересует.

  допилить напильником
Сорри, код на живую стаскал из нескольких классов, работоспособность не проверял, но идея должна быть понятна.

  public sealed class PropertyInfoWrapper<TTarget, TValue>
  {
    private static readonly string getAccessorUndefinedMessage = Resources.PropertyInfoWrapper_GetAccessorUndefined;
    private static readonly string setAccessorUndefinedMessage = Resources.PropertyInfoWrapper_SetAccessorUndefined;

    private const BindingFlags DefaultFlags = BindingFlags.Public
                                            | BindingFlags.NonPublic
                                            | BindingFlags.Static
                                            | BindingFlags.Instance;

    private static TDelegate Create<TDelegate>(MethodInfo method) where TDelegate: class
    {
      return (TDelegate)(object)(Delegate.CreateDelegate(typeof(TDelegate), method));
    }

    private static PropertyInfo GetProperty(Type type, string propertyName)
    {
      PropertyInfo result = type.GetProperty(propertyName, DefaultFlags);

      Code.NotNull(result, "result");
      return result;
    }

    public static PropertyInfoWrapper<TTarget, TValue> WrapProperty(string propertyName)
    {
      PropertyInfo prop = GetProperty(typeof(TTarget), propertyName);

      Func<TTarget, TValue> getCallback = prop.CanRead ?
        Create<Func<TTarget, TValue>>(prop.GetGetMethod(true)) : null;

      Action<TTarget, TValue> setCallback = prop.CanWrite ?
        Create<Action<TTarget, TValue>>(prop.GetSetMethod(true)) : null;

      return new PropertyInfoWrapper<TTarget, TValue>(getCallback, setCallback);
    }

    private readonly Func<TTarget, TValue> getCallback;
    private readonly Action<TTarget, TValue> setCallback;

    public PropertyInfoWrapper(Func<TTarget, TValue> getCallback, Action<TTarget, TValue> setCallback)
    {
      if (getCallback == null)
      {
        Code.NotNull(setCallback, "setCallback");
      }

      this.getCallback = getCallback;
      this.setCallback = setCallback;
    }

    public bool CanGet
    {
      get
      {
        return getCallback != null;
      }
    }
    public bool CanSet
    {
      get
      {
        return setCallback != null;
      }
    }

    public TValue Get(TTarget target)
    {
      Code.AssertState(CanGet, getAccessorUndefinedMessage);
      return getCallback(target);
    }
    public void Set(TTarget target, TValue propertyValue)
    {
      Code.AssertState(CanSet, setAccessorUndefinedMessage);
      setCallback(target, propertyValue);
    }
  }
Re[4]: Reflection, кэширование результатов
От: HowardLovekraft  
Дата: 08.12.10 12:28
Оценка:
Здравствуйте, k.o., Вы писали:

KO>Моему не нужно знать тип свойства на этапе компиляции и он может работать с разными объектами

Кстати, да. У меня на входе — сферический объект в вакууме.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.