2013-05-05 9 views
10

मेरी एस 4 कक्षा में एक विधि है जिसे कई बार कहा जाता है। मैंने देखा कि निष्पादन समय बहुत धीमा है यदि ऐसा होता है तो एक समान कार्य को स्वतंत्र रूप से बुलाया जाता है। तो मैंने अपनी कक्षा में "फ़ंक्शन" प्रकार के साथ एक स्लॉट जोड़ा और विधि के बजाय उस फ़ंक्शन का उपयोग किया। नीचे दिया गया उदाहरण ऐसा करने के दो तरीके दिखाता है, और दोनों संबंधित विधि से बहुत तेज दौड़ते हैं। साथ ही, उदाहरण से पता चलता है कि विधि की निचली गति क्लास से डेटा पुनर्प्राप्त करने के तरीके के कारण नहीं है, क्योंकि जब भी वे ऐसा करते हैं तो फ़ंक्शन तेज़ होते हैं।एस 4 विधि प्रेषण धीमा है?

बेशक, चीजों को करने का यह तरीका आदर्श नहीं है। मुझे आश्चर्य है कि विधि प्रेषण में तेजी लाने का कोई तरीका है या नहीं। कोई सुझाव?

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) 

यह (बेहतर तरीका लुक-अप) एक बहुत ही दिलचस्प और मूल्य-, जबकि परियोजना हो सकता है; अन्य गतिशील भाषाओं में सीखे मूल्यवान सबक हैं, उदाहरण के लिए, विकिपीडिया के dynamic dispatch पृष्ठ पर उल्लिखित इनलाइन कैशिंग।

मुझे आश्चर्य है कि क्या आप कई विधि कॉल कर रहे हैं कारण आपके डेटा प्रतिनिधित्व और विधियों के अधूरे वेक्टरेशन के कारण है?

+0

उपयोगी सुझाव के लिए धन्यवाद। कारण मेरा डेटा प्रतिनिधित्व और विधियां वेक्टरकृत नहीं हैं: मैं बहुरूपता का उपयोग कर रहा हूं। मेरे कोड में मेरे पास एक अलग विधि है। प्रति सबक्लास और विभिन्न लोग अलग-अलग तरीकों को लिख सकते हैं। तो, उदाहरण से अलग, method.foo पर प्रत्येक कॉल एक अलग विधि कहता है और मुझे नहीं पता कि प्रत्येक विधि के शरीर में क्या है। – Soldalma

6

यह आपकी समस्या के साथ सीधे आपकी मदद नहीं करता है, लेकिन यह 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 

अपने कंप्यूटर पर, नंगे कॉल के बारे में 20 एनएस लेता है। एक समारोह में इसे लपेटना एक अतिरिक्त 200 एनएस के बारे में जोड़ता है - यह पर्यावरण बनाने की लागत है जहां फ़ंक्शन निष्पादन होता है। एस 3 विधि प्रेषण 12 μs के आसपास लगभग 3 μs और एस 4/रेफ कक्षाओं को जोड़ता है।

संबंधित मुद्दे