Почему seq (x) намного медленнее, чем 1: length (x)?
Недавно я ответил на вопрос, относящийся к циклам for
. После тестирования скорости моего кода я заметил, что использование seq()
в отличие от :
в цикле for
значительно замедлило скорость.
Взгляните на этот очень простой пример. Единственная разница между 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
Почему seq(x)
настолько медленнее в цикле for
, чем 1:length(x)
?
Ответы
Ответ 1
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
Ответ 2
Используя seq_len
, вы получаете почти то же время, что и оператор :
:
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
выполняет множество проверок перед вызовом :
или seq_len
.
Ответ 3
Более конкретная причина, по которой она медленнее:
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())
}
* Если, конечно, x
не является Date
или POSIXt
, или у вас есть другая библиотека, которая имеет метод seq
...