Правильный подход к кэшированию вычислительно затратного объекта, сгенерированного внутри функции

Фон

В следующем сценарии я рассматриваю следующий рабочий процесс:

  1. Функция бегуна верхнего уровня, в примере ниже running_function, вызывает несколько меньших функций.
  2. Некоторые из этих функций требуют больших вычислительных ресурсов и будут многократно вызываться для одного и того же набора аргументов, так как функции бегуна, по общему мнению, вызываются сценарием верхнего уровня.

Пример

Если не пытаться кэшировать объекты, ситуацию можно резюмировать следующим образом:

Рабочие функции

painful_function <- function(n = 100) {
  matrix(1:n * n, nrow = n)
}

running_function <-
  function(stat_to_do = c("min", "max", "mean", "sum"),
           painful_size = 1e4) {
    stat_to_do <- match.arg(stat_to_do)

    M_pain <- painful_function(n = painful_size)
    do.call(stat_to_do, list(M_pain))

  }

Фактическая работа

# Object M_pain is created inside running_function
running_function(stat_to_do = "min", painful_size = 100)
# I would like to re-use the M_pain object from the previous function
running_function(stat_to_do = "max", painful_size = 100)
# Re-using M_pain again...
running_function(stat_to_do = "mean", painful_size = 100)
# And again ...
running_function(stat_to_do = "sum", painful_size = 100)

Желаемый результат

Идея состоит в том, чтобы не вызывать painful_function более одного раза, поскольку создаваемый им объект идентичен в каждом из сценариев. Therunning_function следует оценивать с предоставленными аргументами.

Подход

Я думал об использовании пакета mustashe:

library("mustashe")
running_function_mstash <-
  function(stat_to_do = c("min", "max", "mean", "sum"),
           painful_size = 1e4) {
    stat_to_do <- match.arg(stat_to_do)

    stash(var = "M_pain",
          code = {
            painful_function(n = painful_size)
          },
          depends_on = "painful_size")
    do.call(stat_to_do, list(M_pain))
}

Это возвращает следующую ошибку:

running_function_mstash(stat_to_do = "min", painful_size = 1e6)

Ошибка в make_hash(depends_on, .TargetEnv): в среде отсутствуют некоторые зависимости.

Вопросы

Мне интересно узнать следующее:

  1. Как заставить это работать, то есть running_function будет выполнять painful_function только в том случае, если один из переданных аргументов изменится, если не полученный объект будет сохранен из файла
  2. Каковы лучшие подходы к использованию этого. Тривиальным методом грубой силы было бы создать временный RDS с причудливым именем и выполнить painful_function только в том случае, если файл не существует. Этот хромой подход имеет и очевидные недостатки. Я хотел бы найти надежное решение, которое охватывает аналогичный работоспособный сценарий.

person Konrad    schedule 26.11.2020    source источник
comment
Почему бы не определить uber_running_function, который вызывает и сохраняет результат painful_function, а затем вызывает running_function по мере необходимости? Все, что вам нужно, это убедиться, что running_function может получить этот объект через параметр data = .   -  person Allan Cameron    schedule 27.11.2020


Ответы (1)


Возможно, объект не обнаруживается. Согласно примеру в ?stash нам нужно использовать <<-

running_function_mstash <-
  function(stat_to_do = c("min", "max", "mean", "sum"),
           painful_size = 1e4) {
    stat_to_do <- match.arg(stat_to_do)
    painful_size <<- painful_size
    stash(var = "M_pain",
          code = {
            painful_function(n = painful_size)
          },
          depends_on = "painful_size")
    do.call(stat_to_do, list(M_pain))
}

running_function_mstash(stat_to_do = "min", painful_size = 1e6)
#Stashing object.
#[1] 1e+06
person akrun    schedule 26.11.2020