Как получить это действительно быстро? - PullRequest
0 голосов
/ 18 ноября 2009

У меня есть структура, которая позволяет пользователям выполнять запросы к определенному источнику данных (игровая база данных 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; }

Как мне заставить это работать хорошо?!

Ответы [ 4 ]

2 голосов
/ 18 ноября 2009

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

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

1 голос
/ 18 ноября 2009

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


заменить

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

с

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

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

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

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

...