【问题标题】:Why are tibbles slower than matrices in a for-loop but faster when accesses very often?为什么 tibbles 在 for 循环中比矩阵慢,但在频繁访问时却更快?
【发布时间】:2019-09-24 06:04:00
【问题描述】:

对于基于代理的建模项目,我正在考虑使用 tidyverse 的 tibble 而不是 matrix。我用一个非常简单的 ABM(见下文)检查了两者的性能,我在其中模拟了一个人口,其中个体正在老化、死亡和出生。典型的 ABM,我使用 for 循环和索引。

在对两种数据结构进行基准测试时(请参见此处的图表:https://github.com/marcosmolla/tibble_vs_matrix),矩阵比 tibble 快得多。然而,对于 10e6 运行,这个结果实际上是相反的。我也不知道为什么。

很高兴了解这个结果以告知我将来是否应该为这种用例使用小标题或矩阵。

感谢大家的任何意见!

# This code benchmarks the speed of tibbles versus matrices. This should be useful for evaluating the suitability of tibbles in a ABM context where matrix data is frequently altered in matrices (or vectors).

library(tidyverse)
library(reshape2)
library(cowplot)

lapply(c(10^1, 10^2, 10^3, 10^4, 10^5, 10^6), function(runtime){
  # Set up tibble
  indTBL <- tibble(id=1:100,
         type=sample(1:3, size=100, replace=T),
         age=1)

  # Set up matrix (from tibble)
  indMAT <- as.matrix(indTBL)

  # Simulation run with tibble
  t <- Sys.time()
  for(i in 1:runtime){
    # increase age
    indTBL$age <- indTBL[["age"]]+1

    # replace individuals by chance or when max age
    dead <- (1:100)[runif(n=100,min=0,max=1)<=0.01 | indTBL[["age"]]>100]
    indTBL[dead, "age"] <- 1
    indTBL[dead, "type"] <- sample(1:3, size=length(dead), replace=T)
  }
  tibbleTime <- as.numeric(Sys.time()-t)

  # Simulation run with matrix
  t <- Sys.time()
  for(i in 1:runtime){
    # increase age
    indMAT[,"age"] <- indMAT[,"age"]+1

    # replace individuals by chance or when max age
    dead <- (1:100)[runif(n=100,min=0,max=1)<=0.01 | indMAT[,"age"]>100]
    indMAT[dead, "age"] <- 1
    indMAT[dead, "type"] <- sample(1:3, size=length(dead), replace=T)
  }
  matrixTime <- as.numeric(Sys.time()-t)

  # Return both run times
  return(data.frame(tibbleTime=tibbleTime, matrixTime=matrixTime))
}) %>% bind_rows() -> res

# Prepare data for ggplot
res$power <- 1:nrow(res)
res_m <- melt(data=res, id.vars="power")

# Line plot for results
ggplot(data=res_m, aes(x=power, y=value, color=variable)) + geom_point() + geom_line() + scale_color_brewer(palette="Paired") + ylab("Runtime in sec") + xlab(bquote("Simulation runs"~10^x))

【问题讨论】:

  • 你自己做过profiling吗?这通常是一个很好的起点。
  • 我怀疑它必须链接到对小标题和data.frames 执行的类型检查。虽然矩阵只能接受单一类型(字符或数字)data.frame 派生对象可以接受更多,因此它们会检查您是否能够对列执行操作。顺便说一句,我建议使用 bench 软件包来精确测量时间(如果您使用 bench,可能还会进行一些内存清理)。

标签: r performance for-loop tidyverse tibble


【解决方案1】:

感谢两位的回复。我使用microbenchmark 包正确地进行基准测试。现在,我发现对于 10e6 运行,矩阵仍然更快。

  indTBL <- tibble(id=1:100,
                   type=sample(1:3, size=100, replace=T),
                   age=1)

  # Set up matrix (from tibble)
  indMAT <- as.matrix(indTBL)

  # Simulation run with tibble
  runtime <- 10^6
  microbenchmark(
  tib=for(i in 1:runtime){
    # increase age
    indTBL$age <- indTBL[["age"]]+1

    # replace individuals by chance or when max age
    dead <- (1:100)[runif(n=100,min=0,max=1)<=0.01 | indTBL[["age"]]>100]
    indTBL[dead, "age"] <- 1
    indTBL[dead, "type"] <- sample(1:3, size=length(dead), replace=T)
  },

  # Simulation run with matrix
  mat=for(i in 1:runtime){
    # increase age
    indMAT[,"age"] <- indMAT[,"age"]+1

    # replace individuals by chance or when max age
    dead <- (1:100)[runif(n=100,min=0,max=1)<=0.01 | indMAT[,"age"]>100]
    indMAT[dead, "age"] <- 1
    indMAT[dead, "type"] <- sample(1:3, size=length(dead), replace=T)
  }, times=1
  )

结果是

Unit: seconds
 expr      min       lq     mean   median       uq      max neval cld
  tib 80.22042 81.45051 82.26645 82.68061 83.28946 83.89831     3   b
  mat 20.44746 20.66974 20.75168 20.89202 20.90378 20.91555     3  a 

感谢 Ilrs 和 MrFlick 的提示。

【讨论】:

    猜你喜欢
    • 2021-12-07
    • 1970-01-01
    • 1970-01-01
    • 2017-11-09
    • 1970-01-01
    • 2021-10-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多