大部分点之前已经提出,但...
sapply()
使用lapply()
然后格式化支付使用simplify2array()
结果的一次性成本。
lapply()
创建一个长的向量,然后创建大量的短(长度为1)的向量,而for循环生成一个单独的长向量。
写入的sapply()
与for循环相比具有额外的函数调用。
使用gcinfo(TRUE)
可以让我们看到垃圾回收器的运行情况,并且每种方法都会导致垃圾回收器多次运行 - 这可能相当昂贵,而且不是完全确定性的。
点1 - 3需要在实施例的人工上下文被解释 - exp()
是一种快速作用,夸大存储器分配(2)中,函数评估(3),和一维的相对贡献时间成本(1)。第4点强调需要以系统的方式重复计时。
我开始加载编译器和microbenchmark软件包。我专注于最大尺寸仅为
library(compiler)
library(microbenchmark)
n <- 10^7
在我的第一个实验中我用简单的分配替换exp()
,并试图代表结果的不同方式的for循环 - 数值的载体,或者数字载体的名单如lapply()
所示。
fun0n <- function(n) {
Y1 <- numeric(n)
for (j in seq_len(n)) Y1[j] <- 1
}
fun0nc <- compiler::cmpfun(fun0n)
fun0l <- function(n) {
Y1 <- vector("list", n)
for (j in seq_len(n)) Y1[[j]] <- 1
}
fun0lc <- compiler::cmpfun(fun0l)
microbenchmark(fun0n(n), fun0nc(n), fun0lc(n), times=5)
## Unit: seconds
## expr min lq mean median uq max neval
## fun0n(n) 5.620521 6.350068 6.487850 6.366029 6.933915 7.168717 5
## fun0nc(n) 1.852048 1.974962 2.028174 1.984000 2.035380 2.294481 5
## fun0lc(n) 1.644120 2.706605 2.743017 2.998258 3.178751 3.187349 5
因此,编译for循环会付出代价,并且生成向量列表的成本相当高。这个内存成本再次被for循环体的简单性所放大。
我的下一个实验探讨不同*apply()
fun2s <- function(n)
sapply(raw(n), function(i) 1)
fun2l <- function(n)
lapply(raw(n), function(i) 1)
fun2v <- function(n)
vapply(raw(n), function(i) 1, numeric(1))
microbenchmark(fun2s(n), fun2l(n), fun2v(n), times=5)
## Unit: seconds
## expr min lq mean median uq max neval
## fun2s(n) 4.847188 4.946076 5.625657 5.863453 6.130287 6.341282 5
## fun2l(n) 1.718875 1.912467 2.024325 2.141173 2.142004 2.207105 5
## fun2v(n) 1.722470 1.829779 1.847945 1.836187 1.845979 2.005312 5
有一个大的成本在sapply()
简化步骤; vapply()
比lapply()
(我保证返回的类型)没有任何性能损失更强大,所以它应该是我在这个家族中的首选功能。
最后,我比较了迭代到vapply()
,其中结果是一个向量列表。
fun1 <- function(n) {
Y1 <- vector("list", n)
for (j in seq_len(n)) Y1[[j]] <- exp(0)
}
fun1c <- compiler::cmpfun(fun1)
fun3 <- function(n)
vapply(numeric(n), exp, numeric(1))
fun3fun <- function(n)
vapply(numeric(n), function(i) exp(i), numeric(1))
microbenchmark(fun1c(n), fun3(n), fun3fun(n), times=5)
## Unit: seconds
## expr min lq mean median uq max neval
## fun1c(n) 2.265282 2.391373 2.610186 2.438147 2.450145 3.505986 5
## fun3(n) 2.303728 2.324519 2.646558 2.380424 2.384169 3.839950 5
## fun3fun(n) 4.782477 4.832025 5.165543 4.893481 4.973234 6.346498 5
microbenchmark(fun1c(10^3), fun1c(10^4), fun1c(10^5),
fun3(10^3), fun3(10^4), fun3(10^5),
times=50)
## Unit: microseconds
## expr min lq mean median uq max neval
## fun1c(10^3) 199 215 230 228 241 279 50
## fun1c(10^4) 1956 2016 2226 2296 2342 2693 50
## fun1c(10^5) 19565 20262 21671 20938 23410 24116 50
## fun3(10^3) 227 244 254 254 264 295 50
## fun3(10^4) 2165 2256 2359 2348 2444 2695 50
## fun3(10^5) 22069 22796 23503 23251 24393 25735 50
编译为for循环和vapply()
是颈部相连;额外的函数调用几乎使vapply()
的执行时间加倍(再一次,这个效果由于示例的简单性而被夸大了)。在一系列尺寸范围内,相对速度似乎没有太大的变化
@Bridgeburners'sapply(...)== simpl2array(lapply(...))'。 'unlist(lapply(...))'呢?为了完整性,我很好奇 – gagolews 2014-10-17 18:07:34
@Bridgeburners:顺便说一句,而不是'plot(log(L),log(t1),type ='l',col ='blue')'try' plot(L,t1,日志=“xy”)' – gagolews 2014-10-17 18:10:29