2014-06-13 56 views
22

我最近回答了有關for循環的問題。在測試我的代碼的速度時,我注意到在for循環中使用seq()而不是:會顯着減慢速度。爲什麼seq(x)比1:length(x)慢得多?

看看這個非常簡單的例子。 f1()f2()之間的唯一區別在於for循環序列的變化,但f1()的速度是f2()的兩倍。

f1 <- function() { 
    x <- 1:5; y <- numeric(length(x)) 
    for(i in 1:length(x)) y[i] <- x[i]^2 
    y 
} 

f2 <- function() { 
    x <- 1:5; y <- numeric(length(x)) 
    for(i in seq(x)) y[i] <- x[i]^2 
    y 
} 

library(microbenchmark) 
microbenchmark(f1(), f2()) 
# Unit: microseconds 
# expr min  lq median  uq max neval 
# f1() 10.529 11.5415 12.1465 12.617 33.893 100 
# f2() 25.052 25.5905 26.0385 28.759 78.553 100 

爲什麼比1:length(x)一個for循環是seq(x)這麼多慢?

回答

24

seq是一個通用的S3方法,所以可能有些時候會丟失調度。 seq.default差不多100行!

你可能已經知道seq_along,這直接調用.Primitive,是位比1:length(x)好,我已經找到了長期循環的最佳方法:

f3 <- function(){ 
     x <- 1:5; y <- numeric(length(x)) 
     for(i in seq_along(x)) y[i] <- x[i]^2 
     y 
    } 
> microbenchmark(f1(), f3()) 
Unit: microseconds 
expr min  lq median  uq max neval 
f1() 27.095 27.916 28.327 29.148 89.495 100 
f3() 26.684 27.505 27.916 28.327 36.538 100 
9

使用​​你會得到幾乎相同的時間作爲:操作:

f3 <- function(){ 
    x <- 1:5; y <- numeric(length(x)) 
    for(i in seq_len(length(x))) y[i] <- x[i]^2 
    y 
} 

library(microbenchmark) 
microbenchmark(f1(), f2(),f3()) 

Unit: microseconds 
expr min  lq median  uq max neval 
f1() 9.988 10.6855 10.9650 11.245 50.704 100 
f2() 23.257 23.7465 24.0605 24.445 88.140 100 
f3() 10.127 10.5460 10.7555 11.175 18.857 100 

內部seq呼籲:或之前做許多核查409315536​​。

1

更具體的原因,它的速度較慢:

seq(x)將調用seq.default *和seq.default電話1L:x

seq.default

if ((One <- nargs() == 1L) && !missing(from)) { 
    lf <- length(from) 
    return(if (mode(from) == "numeric" && lf == 1L) { 
     #checks validity -- more slow-down 
     if (!is.finite(from)) stop("'from' cannot be NA, NaN or infinite") 
     #boom! under the hood, seq.default is doing 1:N 
     1L:from 
    #looks like it defaults to seq_along if length(from) > 1? 
    } else if (lf) 1L:lf else integer()) 
} 

*除非,當然,xDatePOSIXt,或者您有另一個庫加載具有seq方法...

相關問題