2013-05-05 4 views
10

내 S4 클래스에는 여러 번 호출되는 메서드가 있습니다. 비슷한 함수가 독립적으로 호출 된 경우보다 실행 시간이 훨씬 느리다는 것을 알게되었습니다. 그래서 "function"타입의 슬롯을 클래스에 추가하고 메소드 대신이 함수를 사용했습니다. 아래 예제는이 작업을 수행하는 두 가지 방법을 보여 주며 둘 다 해당 메서드보다 훨씬 빠르게 실행됩니다. 또한이 예제는 메서드의 속도가 느리다고해서 메서드가 클래스에서 데이터를 검색해야하기 때문에 함수가 더 빠르기 때문에 메서드가 더 빠르다고 제안합니다.S4 방법이 천천히 발송됩니까?

물론이 일을하는 방식은 이상적이지 않습니다. 방법 파견을 가속화하는 방법이 있는지 궁금합니다. 어떤 제안?

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 

답변

14

비용은 시간의 반복마다 처음부터 시작하는 방법 조회입니다. 이 될 수 번

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

이 (더 나은 방법 룩업)는 매우 흥미롭고 가치-동안 프로젝트가 될 것입니다 방법 파견을 파악하여 단락; Wikipedia의 dynamic dispatch 페이지에서 언급 한 인라인 캐싱과 같은 다른 동적 언어에서 얻은 유용한 교훈이 있습니다.

많은 메소드 호출을하는 이유는 데이터 표현과 메소드의 불완전한 벡터화 때문인가?

+0

유용한 제안을 보내 주셔서 감사합니다. 내 데이터 표현 및 메서드가 벡터화되지 않은 이유 : 다형성을 사용하고 있습니다. 내 코드에는 하위 메서드마다 다른 메서드가 있습니다. 다른 사람들은 다른 메서드를 작성할 수 있습니다. 그래서 예제와 다르게, method.foo에 대한 각각의 호출은 다른 메소드를 호출하고, 각 메소드의 본문에 무엇이 있는지를 알지 못합니다. – Soldalma

6

이 문제와 직접적으로 도움이되지 않지만, 벤치 마크에 마이크로 벤치 패키지 물건 이런 종류의 훨씬 쉽게 내 컴퓨터에서

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 

는 베어 호출은 약 20 ns의합니다. 함수에 랩핑하면 약 200 ns가 추가됩니다. 이것은 함수 실행이 발생하는 환경을 만드는 데 드는 비용입니다. S3 방법 디스패치는 약 3μs 및 S4/ref 클래스를 약 12μs 추가합니다.