आर में छोरों को धीमा क्यों किया जाता है?


86

मुझे पता है कि लूप्स धीमी हैं Rऔर मुझे इसके बजाय एक सदिश तरीके से काम करने की कोशिश करनी चाहिए।

पर क्यों? लूप धीमा क्यों हैं और applyतेज है? applyकई उप-कार्यों को कॉल करता है - जो तेज नहीं लगता है।

अद्यतन: मुझे क्षमा करें, प्रश्न बीमार था। मैं के साथ भ्रामक था apply। मेरा सवाल यह होना चाहिए था,

"सदिशता क्यों तेज है?"


3
मैं इस धारणा के तहत था कि "आर छोरों की तुलना में तेजी से रास्ता लागू होता है" आर एक मिथक का एक सा है । चलो system.timeजवाब में युद्ध शुरू ...
Joran

1
इस विषय पर बहुत अच्छी जानकारी यहाँ है: stackoverflow.com/questions/2275896/…
चेस

7
रिकॉर्ड के लिए: लागू करना वैश्वीकरण नहीं है। लागू एक पाश संरचना है जिसमें विभिन्न (जैसे: नहीं) दुष्प्रभाव होते हैं। @Chase लिंक पर चर्चा देखें।
जोरिस मे

4
S ( S-Plus ?) में लूप्स पारंपरिक रूप से धीमे थे। आर के साथ ऐसा नहीं है ; इस प्रकार, आपका प्रश्न वास्तव में प्रासंगिक नहीं है। मुझे नहीं पता कि एस-प्लस के साथ आज क्या स्थिति है।
गाविन सिम्पसन

4
मेरे लिए यह स्पष्ट नहीं है कि प्रश्न को भारी मतदान क्यों दिया गया है - यह प्रश्न अन्य क्षेत्रों से आर में आने वाले लोगों के बीच बहुत आम है, और एफएक्यू में जोड़ा जाना चाहिए।
patrickmdnet

जवाबों:


69

R में लूप्स उसी कारण से धीमे होते हैं जिस कारण किसी भी व्याख्या की गई भाषा धीमी होती है: प्रत्येक ऑपरेशन में बहुत सारे अतिरिक्त सामान होते हैं।

पर देखो R_execClosureमेंeval.c (इस समारोह उपयोगकर्ता परिभाषित समारोह कॉल करने के लिए कहा जाता है)। यह लगभग 100 लाइनें लंबी है और सभी प्रकार के संचालन करती है - निष्पादन के लिए एक वातावरण बनाना, पर्यावरण में तर्क देना, आदि।

सोचें कि जब आप C में किसी फंक्शन को बुलाते हैं तो कितना कम होता है (पुश स्टैग्स ऑन स्टैक, जंप, पॉप आर्ग्स)।

इसीलिए आपको इस तरह की टाइमिंग मिलती है (जैसा कि ज़ोरान ने टिप्पणी में बताया है, यह वास्तव में applyऐसा नहीं है, यह तेजी से हो रहा है; यह आंतरिक C लूप है mean जो तेजी से हो रहा है। applyयह नियमित रूप से पुराना R कोड है)।

A = matrix(as.numeric(1:100000))

एक लूप का उपयोग करना: 0.342 सेकंड:

system.time({
    Sum = 0
    for (i in seq_along(A)) {
        Sum = Sum + A[[i]]
    }
    Sum
})

राशि का उपयोग:

sum(A)

यह थोड़ा विवादास्पद है क्योंकि, asymptotically, लूप उतना ही अच्छा है sum; इसका कोई व्यावहारिक कारण नहीं है कि यह धीमा होना चाहिए; यह सिर्फ प्रत्येक पुनरावृत्ति के लिए अतिरिक्त काम कर रहा है।

तो विचार करें:

# 0.370 seconds
system.time({
    I = 0
    while (I < 100000) {
        10
        I = I + 1
    }
})

# 0.743 seconds -- double the time just adding parentheses
system.time({
    I = 0
    while (I < 100000) {
        ((((((((((10))))))))))
        I = I + 1
    }
})

(वह उदाहरण रेडफोर्ड नील द्वारा खोजा गया था )

क्योंकि (R में एक ऑपरेटर है, और वास्तव में आपको इसके उपयोग के लिए हर बार नाम खोज की आवश्यकता होती है:

> `(` = function(x) 2
> (3)
[1] 2

या, सामान्य रूप से, व्याख्या किए गए संचालन (किसी भी भाषा में) के अधिक चरण हैं। बेशक, उन कदमों के साथ-साथ लाभ प्रदान करते हैं: क्या आप नहीं कर सका करना है कि (सी में चाल


10
तो आखिर उदाहरण की बात क्या है? R में मूर्खतापूर्ण कार्य न करें और यह अपेक्षा करें कि वे उन्हें जल्दी करें?
चेस

6
@ मुझे लगता है कि यह कहने का एक तरीका है। हाँ, मेरा मतलब था कि सी जैसी भाषा में नेस्टेड कोष्ठकों के साथ कोई गति अंतर नहीं होगा, लेकिन आर अनुकूलन या संकलन नहीं करता है।
ओवेन

1
इसके अलावा (), या {} लूप बॉडी में - इन सभी चीजों में नाम लुकअप शामिल हैं। या सामान्य तौर पर, आर में जब आप अधिक लिखते हैं, तो दुभाषिया अधिक करता है।
ओवेन

1
मुझे यकीन नहीं है कि आप for()छोरों के साथ क्या करने की कोशिश कर रहे हैं ? वे एक ही काम नहीं कर रहे हैं। for()पाश के प्रत्येक तत्व से अधिक पुनरावृत्ति है Aऔर उन्हें संक्षेप। apply()कॉल पूरी सदिश गुजर रहा है A[,1](अपने Aएक vectorised कार्य करने के लिए एक एकल स्तंभ है) mean()। मैं नहीं देखता कि यह कैसे चर्चा में मदद करता है और सिर्फ स्थिति को भ्रमित करता है।
गैविन सिम्पसन

3
@ मैं आपके सामान्य बिंदु से सहमत हूं, और यह एक महत्वपूर्ण है; हम R का उपयोग नहीं करते क्योंकि यह गति रिकॉर्ड तोड़ रहा है, हम इसका उपयोग करते हैं क्योंकि यह उपयोग करना आसान है और बहुत शक्तिशाली है। वह शक्ति व्याख्या की कीमत के साथ आती है। यह केवल स्पष्ट नहीं था कि आप for()बनाम apply()उदाहरण में क्या दिखाने की कोशिश कर रहे थे । मुझे लगता है कि आपको उस उदाहरण को हटा देना चाहिए जब तक कि समष्टि अभिकलन का एक बड़ा हिस्सा है, आपके सभी उदाहरण वास्तव में एक वेक्टरकृत फ़ंक्शन की गति है mean(), तत्वों पर सी-जैसे पुनरावृत्ति।
गैविन सिम्पसन

78

यह हमेशा ऐसा नहीं होता है कि लूप धीमा हो और applyतेज हो। आर न्यूज के मई, 2008 के अंक में इसकी अच्छी चर्चा है :

यूवे लिगेज और जॉन फॉक्स। हेल्प डेस्क: मैं इस पाश से कैसे बच सकता हूं या इसे तेज कर सकता हूं? आर न्यूज़, 8 (1): 46-50, मई 2008।

अनुभाग में "लूप्स!" (पृष्ठ 48 पर शुरू), वे कहते हैं:

आर के बारे में कई टिप्पणियां बताती हैं कि लूप का उपयोग करना विशेष रूप से बुरा विचार है। आवश्यक रूप से यह सही नहीं है। कुछ मामलों में, वेक्टर कोड लिखना मुश्किल है, या वेक्टर कोड भारी मात्रा में मेमोरी का उपभोग कर सकते हैं।

वे आगे सुझाव देते हैं:

  • लूप के भीतर अपने आकार को बढ़ाने के बजाय, लूप से पहले पूरी लंबाई के लिए नई वस्तुओं की शुरुआत करें।
  • एक लूप में ऐसी चीजें न करें जो लूप के बाहर की जा सकती हैं।
  • बस छोरों से बचने के लिए छोरों से बचें।

उनके पास एक सरल उदाहरण है जहां एक forलूप 1.3 सेकंड लेता है लेकिन applyस्मृति से बाहर निकलता है।


35

प्रस्तुत प्रश्न का एकमात्र उत्तर है; यदि आप कुछ कार्य कर रहे हैं और उस फ़ंक्शन या ऑपरेशन को वेक्टरकृत नहीं किया गया है, तो डेटा को सेट करने के लिए क्या करना है अगर छोरियाँ धीमी नहीं हैं । एक लूप सामान्य रूप में, जैसा कि , जल्दी से होगा , लेकिन संभवतः कॉल से थोड़ा धीमा होगा । अंतिम बात अच्छी तरह से इस में, इतने पर कवर उदाहरण के लिए कर रहा है उत्तर , और अगर कोड की स्थापना और संचालन के लिए लागू होता है में शामिल पाश के समग्र कम्प्यूटेशनल बोझ का एक महत्वपूर्ण हिस्सा है पाशfor()apply()lapply()

क्यों कई लोग सोचते for()हैं कि लूप धीमा है क्योंकि वे, उपयोगकर्ता, बुरा कोड लिख रहे हैं। सामान्य तौर पर (हालांकि कई अपवाद हैं), अगर आपको किसी वस्तु का विस्तार / विकास करने की आवश्यकता है, तो उसमें भी नकल शामिल होगी, ताकि आपके पास वस्तु को कॉपी करने और बढ़ने के दोनों ओवरहेड हो । यह केवल लूप्स तक ही सीमित नहीं है, लेकिन यदि आप लूप के प्रत्येक पुनरावृत्ति पर कॉपी / ग्रो करते हैं, तो निश्चित रूप से, लूप धीमा होने वाला है क्योंकि आप कई कॉपी / ग्रो ऑपरेशंस को कर रहे हैं।

for()आर में लूप का उपयोग करने के लिए सामान्य मुहावरा है कि आप लूप शुरू होने से पहले आवश्यक भंडारण आवंटित करें, और फिर आवंटित वस्तु में भरें। यदि आप उस मुहावरे का अनुसरण करते हैं, तो लूप धीमा नहीं होगा। यह वही है जो apply()आपके लिए प्रबंधित करता है, लेकिन यह केवल दृश्य से छिपा हुआ है।

बेशक, यदि आप जिस ऑपरेशन के लिए for()लूप के साथ कार्य कर रहे हैं, उसके लिए एक सदिश फ़ंक्शन मौजूद है , तो ऐसा न करें । इसी तरह, अगर एक सदिश फ़ंक्शन मौजूद है (जैसे बेहतर प्रदर्शन किया जाता है ) आदि का उपयोग करें ।apply()apply(foo, 2, mean)colMeans(foo)


9

बस एक तुलना के रूप में (इसमें बहुत अधिक न पढ़ें!): मैंने क्रोम और IE में आर में और जावास्क्रिप्ट में लूप के लिए एक (बहुत) सरल भाग लिया। ध्यान दें कि क्रोम मूल कोड का संकलन करता है, और आर कंपाइलर के साथ आर। पैकेज बाइटकोड को संकलित करता है।

# In R 2.13.1, this took 500 ms
f <- function() { sum<-0.5; for(i in 1:1000000) sum<-sum+i; sum }
system.time( f() )

# And the compiled version took 130 ms
library(compiler)
g <- cmpfun(f)
system.time( g() )

@ गेविन सिम्पसन: Btw, एस-प्लस में 1162 एमएस लिया ...

और जावास्क्रिप्ट के रूप में "वही" कोड:

// In IE8, this took 282 ms
// In Chrome 14.0, this took 4 ms
function f() {
    var sum = 0.5;
    for(i=1; i<=1000000; ++i) sum = sum + i;
    return sum;
}

var start = new Date().getTime();
f();
time = new Date().getTime() - start;
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.