正确使用Microbenchmark功能

sta*_*oob 1 r

我正在尝试学习如何在 R 中使用 Microbenchmark 函数。

作为示例,我模拟了一些不同大小的随机数据集:

# load the lubridate package
library(lubridate)
library(microbenchmark)
library(forecast)

my_list = list()
index =  c(100, 1000, 10000, 50000, 100000, 250000, 500000, 750000, 1000000)



for (i in 1:length(index))
{

my_data_i = data.frame(dates = sample(seq(as.Date('2010/01/01'), as.Date('2023/01/01'), by="day"), replace = TRUE, index[i]), visits = 1)
my_list[[i]] = my_data_i

}
Run Code Online (Sandbox Code Playgroud)

然后我创建了一个我想在每个数据集上重复测量的函数:

my_function = function(){
# aggregate the data by week
my_data_i_weekly <- aggregate(my_data_i$visits, list(week = week(my_data_i$dates), year = year(my_data_i$dates)), sum)

# convert the data frame to a time series
my_data_i_ts <- ts(my_data_i_weekly$x, start = c(min(my_data_i_weekly$week), min(my_data_i_weekly$year)), frequency = 52)

# fit an ARIMA model using auto.arima
my_data_i_arima <- auto.arima(my_data_i_ts)

}
Run Code Online (Sandbox Code Playgroud)

在过去,我会手动计时每次迭代 - 例如:

results = list()
for (i in length(index))
{
    start.time_i <- Sys.time()
    my_data_i = my_list[[i]]
    print(replicate(n = 100, my_function())
          end.time_i <- Sys.time()
          time.taken_i <- end.time_i - start.time_i
          results[[i]] = time_taken_i
}
Run Code Online (Sandbox Code Playgroud)

现在,我正在尝试学习如何使用 R 中的“microbenchmark”函数来做到这一点。

my_list2 = list()

for (i in 1:length(index))
{
my_data_i = my_list[[i]]
res_i = microbenchmark(my_function(), times = 100)
print(res_i)
my_list2[[i]] = res_i
}
Run Code Online (Sandbox Code Playgroud)

回顾一下 - 我正在尝试执行以下操作:

  • 在 my_data[ 1 ]上运行“my_function()” 100 次并记录花费的时间
  • 在 my_data[[2]] 上运行“my_function()”100 次并记录花费的时间
  • ETC。

我这样做正确吗?

谢谢!

注意:将来,我想制作这样的图表(例如红线 - 计算机 1,绿线 - 计算机 2):

在此输入图像描述

Tim*_*Fan 6

我知道这个问题是关于 {microbenchmark} 包,但我认为 {bench} 包更适合您所需的输出。

根据这篇博客文章,它在以下方面优于其他基准测试包:

  • 使用每个操作系统可用的最高精度 API(通常是纳秒级)。
  • 跟踪每个表达式的内存分配。
  • 跟踪每次运行的 R 垃圾收集的数量和类型。
  • 默认情况下验证表达式结果的相等性,以避免意外对非等效代码进行基准测试。
  • 默认情况下使用自适应停止,将每个表达式运行设定的时间而不是特定的迭代次数。
  • 批量运行表达式,并在通过垃圾收集过滤掉迭代后计算汇总统计信息。这使您可以隔离垃圾收集的性能和对运行时间的影响(有关更多详细信息,请参阅 Neal 2014)。
  • 允许使用 bench::press() 在输入值网格上进行基准测试。

应用于您的设置,我首先将重写my_function如下:

my_function = function(df){
  # aggregate the data by week
  my_data_i_weekly <- aggregate(df$visits, list(week = week(df$dates), year = year(df$dates)), sum)
  
  # convert the data frame to a time series
  my_data_i_ts <- ts(my_data_i_weekly$x, start = c(min(my_data_i_weekly$week), min(my_data_i_weekly$year)), frequency = 52)
  
  # fit an ARIMA model using auto.arima
  auto.arima(my_data_i_ts)
}
Run Code Online (Sandbox Code Playgroud)

现在我们可以创建一个函数来生成data.frame具有给定数量的行:

create_df <- function(rows) {
  data.frame(dates = sample(seq(as.Date('2010/01/01'), as.Date('2023/01/01'), by="day"),
                            replace = TRUE,
                            rows),
             visits = 1)
}
Run Code Online (Sandbox Code Playgroud)

现在我们可以使用bench::press()循环遍历行号列表并bench::mark()在内部使用。请注意,我们可以添加另一个函数,例如my_fun2(),来比较不同大小的数据集与 的性能my_fun()

library(lubridate)
library(forecast)
library(bench)

results <- bench::press(
  rows = c(10, 100, 1000),
  {
    dat <- create_df(rows)
    bench::mark(
      min_iterations = 10,
      my_fun = my_function(dat)
    )
  }
)

results
#> # A tibble: 3 x 7
#>   expression  rows      min   median `itr/sec` mem_alloc `gc/sec`
#>   <bch:expr> <dbl> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#> 1 my_fun        10   3.57ms   3.75ms   252.       2.53MB     4.13
#> 2 my_fun       100  23.69ms  25.87ms    38.9      3.48MB     7.29
#> 3 my_fun      1000    1.21s    1.23s     0.765  994.53MB     4.66
Run Code Online (Sandbox Code Playgroud)

最后,我们可以通过两种方式使用 {ggplot2} 打印结果:

library(ggplot2)
library(ggbeeswarm)
library(bench)

ggplot2::autoplot(results)
Run Code Online (Sandbox Code Playgroud)

results |>
  ggplot(aes(x = rows,
             y = median,
             group = expression,
             colour = expression)) +
  geom_line() +
  scale_y_bench_time(base = NULL)
Run Code Online (Sandbox Code Playgroud)

创建于 2023-03-03,使用reprex v2.0.2