2013-05-05 43 views
10

Lớp S4 của tôi có một phương thức được gọi nhiều lần. Tôi nhận thấy rằng thời gian thực hiện chậm hơn nhiều so với khi một chức năng tương tự được gọi là độc lập. Vì vậy, tôi đã thêm một vị trí có loại "hàm" vào lớp của tôi và sử dụng hàm đó thay vì phương thức. Ví dụ dưới đây cho thấy hai cách làm điều này, và cả hai cách này chạy nhanh hơn nhiều so với phương thức tương ứng. Ngoài ra, ví dụ cho thấy rằng tốc độ thấp hơn của phương pháp không phải là do phương pháp phải lấy dữ liệu từ lớp, vì các hàm nhanh hơn ngay cả khi chúng cũng làm điều đó.Phương pháp S4 có được gửi chậm không?

Tất nhiên, cách làm việc này không lý tưởng. Tôi tự hỏi nếu có một cách để tăng tốc công văn phương pháp. Bất kỳ đề xuất?

setClass(Class = "SpeedTest", 
     representation = representation(
     x = "numeric", 
     foo1 = "function", 
     foo2 = "function" 
    ) 
    ) 

    speedTest <- function(n) { 
     new("SpeedTest", 
     x = rnorm(n), 
     foo1 = function(z) sqrt(abs(z)), 
     foo2 = function() {} 
    ) 
    } 

    setGeneric(
     name = "method.foo", 
     def = function(object) {standardGeneric("method.foo")} 
    ) 
    setMethod(
     f = "method.foo", 
     signature = "SpeedTest", 
     definition = function(object) { 
     sqrt(abs([email protected])) 
     } 
    ) 

    setGeneric(
     name = "create.foo2", 
     def = function(object) {standardGeneric("create.foo2")} 
    ) 
    setMethod(
     f = "create.foo2", 
     signature = "SpeedTest", 
     definition = function(object) { 
     z <- [email protected] 
     [email protected] <- function() sqrt(abs(z)) 

     object 
     } 
    ) 

    > st <- speedTest(1000) 
    > st <- create.foo2(st) 
    > 
    > iters <- 100000 
    > 
    > system.time(for (i in seq(iters)) method.foo(st)) # slowest by far 
     user system elapsed 
     3.26 0.00 3.27 

    > # much faster 
    > system.time({foo1 <- [email protected]; x <- [email protected]; for (i in seq(iters)) foo1(x)}) 
     user system elapsed 
     1.47 0.00 1.46 

    > # retrieving [email protected] instead of x does not affect speed 
    > system.time({foo1 <- [email protected]; for (i in seq(iters)) foo1([email protected])}) 
     user system elapsed 
     1.47 0.00 1.49 

    > # same speed as foo1 although no explicit argument 
    > system.time({foo2 <- [email protected]; for (i in seq(iters)) foo2()}) 
     user system elapsed 
     1.44 0.00 1.45 

    # Cannot increase speed by using a lambda to "eliminate" the argument of method.foo 
    > system.time({foo <- function() method.foo(st); for (i in seq(iters)) foo()}) 
     user system elapsed 
     3.28 0.00 3.29 

Trả lời

14

Chi phí nằm trong phương pháp tra cứu, bắt đầu từ đầu trong mỗi lần lặp lại thời gian của bạn. Điều này có thể được đoản mạch bằng cách tìm ra công văn phương thức một lần

METHOD <- selectMethod(method.foo, class(st)) 
for (i in seq(iters)) METHOD(st) 

Đây là một dự án rất thú vị và đáng giá; có các bài học có giá trị được học trong các ngôn ngữ động khác, ví dụ: bộ nhớ đệm nội tuyến được đề cập trên trang dynamic dispatch của Wikipedia.

Tôi tự hỏi lý do bạn đang thực hiện nhiều cuộc gọi phương thức là do việc vector hóa không đầy đủ về cách trình bày và phương pháp dữ liệu của bạn không?

+0

Cảm ơn đề xuất hữu ích. Lý do dữ liệu của tôi đại diện và phương pháp không vectorized: Tôi đang sử dụng đa hình. Trong mã của tôi, tôi có một method.foo khác nhau cho mỗi lớp con và những người khác nhau có thể viết các phương thức khác nhau. Vì vậy, khác với ví dụ, mỗi lời gọi hàm method.foo gọi một phương thức khác và tôi không biết cái gì nằm trong phần thân của mỗi phương thức. – Soldalma

6

này không giúp bạn trực tiếp với vấn đề của bạn, nhưng nó dễ dàng hơn để chuẩn loại công cụ này với gói microbenchmark:

f <- function(x) NULL 

s3 <- function(x) UseMethod("s3") 
s3.integer <- function(x) NULL 

A <- setClass("A", representation(a = "list")) 
setGeneric("s4", function(x) standardGeneric("s4")) 
setMethod(s4, "A", function(x) NULL) 

B <- setRefClass("B") 
B$methods(r5 = function(x) NULL) 

a <- A() 
b <- B$new() 

library(microbenchmark) 
options(digits = 3) 
microbenchmark(
    bare = NULL, 
    fun = f(), 
    s3 = s3(1L), 
    s4 = s4(a), 
    r5 = b$r5() 
) 
# Unit: nanoseconds 
# expr min lq median uq max neval 
# bare 13 20  22 29 36 100 
# fun 171 236 270 310 805 100 
# s3 2025 2478 2651 2869 8603 100 
# s4 10017 11029 11528 11905 36149 100 
# r5 9080 10003 10390 10804 61864 100 

Trên máy tính của tôi, cuộc gọi trọc mất khoảng 20 ns. Bao bọc nó trong một hàm bổ sung thêm khoảng 200 ns - đây là chi phí tạo môi trường nơi thực hiện hàm xảy ra. Phương thức S3 gửi thêm khoảng 3 µs và S4/ref khoảng 12 µs.