Подходы для кэширования вычисляемых значений - PullRequest
8 голосов
/ 08 октября 2009

В приложении 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, чтобы другие могли подписываться, получать уведомления при изменении и сбрасывать кэшированные значения. Этот подход работает, но имеет некоторые недостатки:

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

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

Ответы [ 3 ]

4 голосов
/ 08 октября 2009

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

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

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

Например, SCons (альтернатива Makefile) делает это, чтобы проверить, нужно ли восстанавливать цель, желательно с использованием временного метки.

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

2 голосов
/ 08 октября 2009

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

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

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

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.
1 голос
/ 08 октября 2009

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

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

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

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

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

Edit: На самом деле есть ссылка для загрузки для зарегистрированных пользователей Embarcadero для Bold для Delphi для D7, довольно старая ... Я знаю, что были обновления для D2005, объявление D2006.

...