2014-06-13 21 views
22

Gần đây tôi đã trả lời một câu hỏi liên quan đến các vòng for. Sau khi kiểm tra tốc độ mã của tôi, tôi nhận thấy rằng việc sử dụng seq() như trái ngược với : trong vòng lặp for làm chậm tốc độ xuống đáng kể.Tại sao seq (x) chậm hơn rất nhiều so với 1: độ dài (x)?

Hãy xem ví dụ rất đơn giản này. Sự khác biệt duy nhất giữa f1()f2() là thay đổi trong chuỗi vòng lặp for, nhưng f1() nhanh hơn hai lần là 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 

Tại sao seq(x) nên chậm hơn trong một vòng lặp hơn 1:length(x)for?

Trả lời

24

seq là phương pháp S3 chung, do đó có thể mất một số thời gian để gửi đi. seq.default dài gần 100 dòng!

Bạn đang có lẽ đã nhận thức được seq_along, trong đó kêu gọi một .Primitive trực tiếp và là chút tốt hơn so với 1:length(x) và phương pháp tốt nhất mà tôi đã tìm thấy cho vòng dài:

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

Sử dụng seq_len bạn nhận được gần như cùng một lúc như : điều hành:

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 

Bên seq đang làm nhiều thẩm tra trước khi gọi : hoặc seq_len.

1

Một lý do cụ thể hơn lý do tại sao nó chậm:

seq(x) sẽ gọi seq.default *, và seq.default cuộc gọi 1L:x !!

Từ 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()) 
} 

* Trừ khi, tất nhiên, xDate hay POSIXt, hoặc bạn đã thư viện khác nạp mà có một phương pháp seq ...

Các vấn đề liên quan