Bezdepbonusu7Программирование: статьи и ответы

Вопрос

Как получить это очень быстро?

Автор вопроса: Jan JongboomОтветов: 4

У меня есть структура, которая позволяет пользователям выполнять запросы к определенному источнику данных (внутриигровая база данных Football Manager 2010, для тех из вас, кто заинтересован).

В этой структуре у меня есть два разных режима, в которых может работать моя структура: режим реального времени и режим кэширования. Я хочу, чтобы пользователи, использующие этот фреймворк, могли переключаться, просто вызывая другой конструктор (например, new Context(Mode.Cached)). Это должен быть единственный переключатель, который должен сделать пользователь, чтобы он мог по-прежнему иметь все те же вызовы Linq, но просто использовать режим кэширования, когда его приложение лучше подходит. Прозрачный.

Я решил, что использование PostSharp должно быть моим лучшим выбором, потому что:

  • Создайте аспект для каждого свойства (которое уже было оформлено атрибутом)
  • В этом аспекте проверьте, находимся ли мы в режиме Cached или Realtime
  • Вернуть значение либо из памяти, либо из кеша

Что ж, это работает. НО! Скорость недостаточно хороша. При выполнении следующих действий на 90 000 объектов:

foreach (Player p in fm.Players)
{
    int ca = (short)ProcessManager.ReadFromBuffer(p.OriginalBytes, PlayerOffsets.Ca, typeof(Int16));
}

Это занимает всего 63 мс. (ReadFromBuffer - это высокооптимизированная функция, которая принимает byte[], int, Type и возвращает object), 63 мс - это очень разумно, учитывая большое количество объектов.

Но! В PostSharp я реализовал то же самое, используя это:

    public override void OnInvocation(MethodInvocationEventArgs eventArgs)
    {
        if (eventArgs.Method.Name.StartsWith("~get_"))
        {
            if (Global.DatabaseMode == DatabaseModeEnum.Cached)
            {
                byte[] buffer = ((BaseObject)eventArgs.Instance).OriginalBytes;

                eventArgs.ReturnValue =
                        ProcessManager.ReadFromBuffer(buffer, this.Offset, eventArgs.Method.ReturnType);
            }

Теперь я называю это с помощью

foreach (Player p in fm.Players)
{
    int ca = p.CA;
}

И это занимает 782 мс, то есть более чем в 10 раз больше!

Я создал аспект как:

[Serializable]
[MulticastAttributeUsage(MulticastTargets.Method, PersistMetaData = true)]
internal class FMEntityAttribute : OnMethodInvocationAspect
{
    public FMEntityAttribute(int offset, int additionalStringOffset)
    {
        this.Offset = offset;
        this.AdditionalStringOffset = additionalStringOffset;
    }
    //blah blah AOP code
}

И собственность оформлена как

    [FMEntityAttribute(PlayerOffsets.Ca)]
    public Int16 CA { get; set; }

Как я могу заставить это работать хорошо?!

  • Что, согласно вашему профилированию, было дорогой точкой доступа? Eric Lippert 18.11.2009
  • То, что больше всего времени было потрачено на получение моего свойства, не очень полезная информация. Jan Jongboom 18.11.2009

aoppostsharp

Ответы

Ответ 1 · рейтинг 1 · Pete Kirkham

Вместо того, чтобы создавать свой контекст с помощью new Context(Mode.Cached)), используйте фабричный метод, который создает контекст. Затем реализуйте два ваших поведения в двух разных классах, которые разделяют все, что им нужно от абстрактного супертипа. Используйте аспекты и рефлексию для решения проблем, которые не имеют простого прямого решения.


заменять

[FMEntityAttribute(PlayerOffsets.Ca)] public Int16 CA { get; }

с участием

public Int16 CA { get { return PlayerAttrs.Ca.Get(this); } }

где PlayerAttrs имеет оператор Int16 для преобразования себя в Int16 по запросу, имеет требуемое смещение и выполняет соответствующий кэшированный/некэшированный поиск.

Ответ 2 · рейтинг 2 · Gael Fraiteur

Вы можете получить гораздо лучшие результаты, используя LocationInterceptionAspect PostSharp 2.0.

Но тогда вам следует избегать использования eventArgs.Method.ReturnType во время выполнения; скорее получите значение в методе RuntimeInitialize и сохраните его в поле. Таким образом, System.Reflection не используется во время выполнения.

Ответ 3 · рейтинг 1 · Community

  1. Используйте метод CompileTimeValidate, чтобы проверить, является ли это свойством или нет.

Ответ 4 · Chris Pitman

Отражение может быть дорогим. Одна вещь, которую вы можете попробовать, - это скомпилировать оболочку для этого класса во время выполнения и сохранить себе попадание для каждого вызова, которое у вас есть в настоящее время.

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы