Подтвердить что ты не робот

Почему 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)?

4b9b3361

Ответ 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...