функции `микробенчмарка`, созданные фабричной функцией

У меня есть функция, которая создает другую функцию в соответствии с некоторыми конкретными параметрами:

factory <- function(param) {
    # some long computation…
    cached.matrix = rnorm(param)

    # return function that uses cached data
    function(other) {
        cached.matrix * cached.matrix + other
    }
}

Теперь я хотел протестировать функции, сгенерированные функцией factory, для различных параметров, используя пакет microbenchmark. Функция microbenchmark принимает выражение, а не функцию, и после некоторых экспериментов я заметил, что следующий танец с do.call и call вроде бы работает:

params <- seq(5, 100, 5)
list.of.expressions <- sapply(
    params,
    function(param) call('do.call', factory(param), list(7)))
names(list.of.expressions) <- sprintf("f%04d", params)
mb.results <- microbenchmark(list=list.of.expressions, times=10)

Есть ли более простой способ сбора результатов теста функций, параметризованных таким образом, чем call('do.call', …)?


person liori    schedule 09.04.2015    source источник


Ответы (1)


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

Я неявно предполагаю, что вы хотели бы выполнить итерацию по двум измерениям (в вашем примере param и other). Я построил свою собственную фабрику функций:

factory <- function(param) {
  x <- rnorm(param)
  function(mat) {
    t(x) %*% mat %*% x
  }
}

Теперь я хотел бы перебрать param и mat. Чтобы было еще интереснее, пусть mat как-то зависит от param. Если это не так, просто игнорируйте mat как функцию: это может быть вектор.

params <- seq(10, 50, 10)
mat1 <- function(param) {diag(param)}
mat2 <- function(param) {matrix(runif(param^2), param)}

Вот функция, которая перейдёт к outer, и сам вызов:

test_factory_med <- Vectorize(function(param, matf) {
         summary(microbenchmark(factory(param)(matf(param))))$median
})
median_tests <- outer(params, c(mat1, mat2), 
                      FUN = function(p, m) test_factory_med(p, m))

colnames(median_tests) <- c("mat1", "mat2")
rownames(median_tests) <- params
median_tests
#      mat1     mat2
#10 15.3150  22.6720
#20 18.6180  36.6355
#30 22.2220  57.9560
#40 27.3265  88.5860
#50 32.7320 129.1250

Вы можете сохранить полный набор информации из microbenchmark, вернув из него список (с помощью хака с переносом списка из моего последний вопрос):

test_factory_all <- Vectorize(function(param, matf) {
  list(
    list(param = param, 
         matf = mat,
         microbenchmark(factory(param)(matf(param)))))
})

all_tests <- outer(params, c(mat1, mat2), 
                   FUN = function(p, m) test_factory_all(p, m))
all_tests[1, 1]

#[[1]]
#[[1]]$param
#[1] 10
#
#[[1]]$matf
#function (param) 
#{
#    diag(param)
#}
#
#[[1]][[3]]
#Unit: microseconds
#                        expr    min     lq     mean median     uq    max neval
# factory(param)(matf(param)) 14.414 15.315 17.17081 15.916 16.516 88.586   100

Изменить: в ответ на комментарий ниже, вот как вы можете измерить только вызовы функций, которые вышли с завода.

# exclude costs for running factory
test_factory_med1 <- Vectorize(function(param, matf) {
         f <- factory(param)
         summary(microbenchmark(f(matf(param))))$median
})

# exclude costs for both running factory and constructing parameter
test_factory_med2 <- Vectorize(function(param, matf) {
         f <- factory(param)
         m <- matf(param)
         summary(microbenchmark(f(m)))$median
})
person tonytonov    schedule 10.04.2015
comment
Хм, я не хочу тестировать фабричную функцию, только результат. Не будет ли microbenchmark(factory(param)(matf(param))) подсчитываться время как фабричного вызова, так и возвращаемого вызова функции? - person liori; 10.04.2015
comment
@liori Да, будет. Разве это не то, что вам нужно? Что вы подразумеваете под эталоном только результат? - person tonytonov; 10.04.2015
comment
Если вас интересует только сравнительный анализ окончательного вызова (за вычетом стоимости factory(param), я полагаю, это то, что вы имели в виду выше), поместите factory(param) из microbenchmark: fp <- factory(param); microbenchmark(fp(matf(param))) - person tonytonov; 10.04.2015
comment
Да, меня интересует именно это — иначе я бы вообще не писал фабричную функцию. Спасибо; так что, по сути, вы объединяете результаты нескольких microbenchmark запусков вместо того, чтобы, как я пытался, запускать один большой. Это хорошая подсказка. Это упрощает простое помещение результата фабричной функции в среду, так что вы можете просто вызывать ее по имени внутри вызова microbenchmark. - person liori; 10.04.2015
comment
Логично, согласен. Как видно из редактирования, общую концепцию можно легко адаптировать практически ко всему, что вы хотите включить или исключить из своих измерений. - person tonytonov; 10.04.2015