2009-10-08 2 views
8

В приложении Delphi мы работаем над тем, что у нас есть большая структура связанных объектов. Некоторые свойства этих объектов имеют значения, которые вычисляются во время выполнения, и я ищу способ кэшировать результаты для более интенсивных вычислений. Подход, который я использую, сохраняет значение в частном члене при первом вычислении. Вот краткий пример:Подходы для кэширования расчетных значений

unit Unit1; 

interface 

type 
    TMyObject = class 
    private 
    FObject1, FObject2: TMyOtherObject; 
    FMyCalculatedValue: Integer; 
     function GetMyCalculatedValue: Integer; 
    public 
    property MyCalculatedValue: Integer read GetMyCalculatedValue; 
    end; 

implementation 

    function TMyObject.GetMyCalculatedValue: Integer; 
    begin 
    if FMyCalculatedValue = 0 then 
    begin 
     FMyCalculatedValue := 
     FObject1.OtherCalculatedValue + // This is also calculated 
     FObject2.OtherValue; 
    end; 

    Result := FMyCalculatedValue; 
    end; 

end. 

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

  • Для управления подписками требуется много памяти.
  • Он плохо масштабируется, когда кешированное значение зависит от множества объектов (например, списка).
  • Зависимость не очень конкретна (даже если значение кеша зависит только от одного свойства, оно будет сброшено также при изменении других свойств).
  • Управление подписками влияет на общую производительность и сложно поддерживать (объекты удаляются, перемещаются, ...).
  • Непонятно, как бороться с расчетами в зависимости от других рассчитанных значений.

И, наконец, вопрос: можете ли вы предложить другие подходы для реализации вычисленных значений кеширования?

+0

Даже если отмечено «delphi», мне очень интересно узнать, был ли развит определенный образец. –

+0

Я добавил тег Delphi так, чтобы он ограничивал предложения статическим типизированным, а не сборщиком мусора. – Tihauan

ответ

1

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

Код такого атрибута написан в Delphi как процедура или в OCL (Object Constraint Language) в модели. Если вы напишете его как код Delphi, вам нужно подписаться на переменные переменные. Поэтому, если атрибут C зависит от A и B, то при каждом изменении A или B код recalc C вызывается автоматически при чтении C. Таким образом, первый раз, когда C читается, A и B также читаются (возможно, из базы данных). Пока A и B не изменены, вы можете прочитать C и получить очень высокую производительность. Для сложных вычислений это может сэкономить довольно много процессорного времени.

Недостатком и плохими новостями является то, что Bold больше не поддерживается в оффлайне, и вы также не можете его купить. Я полагаю, вы можете получить, если вы спросите достаточно людей, но я не знаю, где вы можете скачать его. Примерно в 2005-2006 годах он был загружен бесплатно из Borland, но больше не доступен. Он не готов к D2009, так как кто-то должен его перенести в Юникод.

Другой вариант: ECO с dot.net от Capable Objects. ECO - это плагин в Visual Studio. Это поддерживаемая фреймворк, который имеет ту же идею и автора, что и Bold для Delphi. Многие вещи также улучшены, например, привязка данных используется для GUI-компонентов. Как Bold, так и ECO используют модель как центральную точку с классами, атрибутами и ссылками. Они могут сохраняться в базе данных или в XML-файле. С бесплатной версией ECO модель может иметь максимум 12 классов, но, как я помню, других ограничений нет.

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

Edit: Существует на самом деле скачать link for Embarcadero registred users для Жирный для Delphi для D7, довольно старый ... Я знаю, что было обновления для D2005, D2006 объявлений.

+0

Модельные фреймворки в целом и «Смелые для Delphi» в частности звучат очень интересно. Спасибо! – Tihauan

+0

На самом деле я нашел Bold для D2006 (я думаю, что это последняя общедоступная версия) на одном из моих жестких дисков, поэтому, если вам интересно, просто напишите мне на [email protected], поэтому я могу его отправить. Или Skype, мой идентификатор - d98rolb. –

+0

Tihauan, для получения дополнительной информации и небольшого примера, как производные значения работают в Bold, посмотрите мой блог на странице http://boldfordelphi.blogspot.com/#derattr. –

4

Если вы хотите избежать шаблона наблюдения, вы можете попытаться использовать подход хэширования.

Идея заключалась бы в том, что вы «hash» аргументы и проверяете, соответствует ли это «хэш», для которого сохраняется состояние. Если это не так, то вы перепродаете (и тем самым сохраните новый хэш как ключ).

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

Например, SCons (альтернативный вариант Makefile) позволяет проверить, требуется ли повторная сборка цели, ориентированной на метку времени.

Мы использовали SCons уже более года, и мы не обнаружили никаких проблем с целью, которая не была перестроена, поэтому их хэш работает хорошо!

+0

Просто убедитесь, что вычисление хеша (или любого другого метода, который вы выберете) (значительно) быстрее, чем пересчет. –

+0

Да, я не указывал на это, поскольку это казалось очевидным, но, как всегда, с оптимизацией ... вам действительно нужно измерить. –

2

Вы можете сохранить локальные копии значений внешних объектов, которые требуются. Затем процедура доступа сравнивает локальную копию с внешним значением и только перерасчет изменений.

Доступ к свойствам внешних объектов также приведет к возможной переоценке этих свойств, поэтому система должна автоматически обновляться, но только пересчитывать, когда это необходимо. Я не знаю, нужно ли вам принимать меры, чтобы избежать круговых зависимостей.

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

unit Unit1; 

interface 

type 
    TMyObject = class 
    private 
    FObject1, FObject2: TMyOtherObject; 
    FObject1Val, FObject2Val: Integer; 
    FMyCalculatedValue: Integer; 
     function GetMyCalculatedValue: Integer; 
    public 
    property MyCalculatedValue: Integer read GetMyCalculatedValue; 
    end; 

implementation 

    function TMyObject.GetMyCalculatedValue: Integer; 
    begin 
    if (FObject1.OtherCalculatedValue <> FObjectVal1) 
    or (FObject2.OtherValue <> FObjectVal2) then 
    begin 
     FMyCalculatedValue := 
     FObject1.OtherCalculatedValue + // This is also calculated 
     FObject2.OtherValue; 
     FObjectVal1 := FObject1.OtherCalculatedValue; 
     FObjectVal2 := Object2.OtherValue; 
    end; 

    Result := FMyCalculatedValue; 
    end; 

end. 
+0

Я также хотел бы убедиться, что fObject1 и fObject2 назначены до выполнения расчета ... просто для того, чтобы быть в безопасности. – skamradt

+0

@skamradt: согласовано. Я предположил, что вопрос оставил проверку достоверности ввода/ошибки, чтобы код примера был прост. – IanH

Смежные вопросы