क्या रूबी टेल कॉल ऑप्टिमाइज़ेशन करती है?


92

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

रूबी ने स्पष्ट रूप से कार्यात्मक भाषाओं (लैम्ब्डा, फ़ंक्शंस जैसे मानचित्र और आगे, आदि) से कई अवधारणाओं को "उधार" लिया है, जो मुझे उत्सुक बनाता है: क्या रूबी पूंछ कॉल अनुकूलन करता है?

जवाबों:


127

नहीं, रूबी TCO प्रदर्शन नहीं करती है। हालाँकि, यह TCO प्रदर्शन नहीं करता है ।

रूबी भाषा विनिर्देश TCO के बारे में कुछ नहीं कहता है। यह नहीं कहता कि आपको यह करना है, लेकिन यह भी नहीं कहता कि आप इसे नहीं कर सकते । आप सिर्फ इस पर भरोसा नहीं कर सकते ।

यह स्कीम के विपरीत है, जहां भाषा विशिष्टता के लिए आवश्यक है कि सभी कार्यान्वयन TCO को निष्पादित करें। लेकिन यह पाइथन के विपरीत भी है, जहां गुइडो वैन रोसुम ने कई मौकों पर (पिछली बार सिर्फ कुछ दिनों पहले) यह स्पष्ट कर दिया है कि पायथन इम्प्लीमेंटेशन TCO नहीं करना चाहिए

युकीहिरो मात्सुमोतो को TCO से सहानुभूति है, वह सिर्फ इसे लागू करने के लिए सभी कार्यान्वयनों को बाध्य नहीं करना चाहता है। दुर्भाग्य से, इसका मतलब है कि आप TCO पर भरोसा नहीं कर सकते, या यदि आप करते हैं, तो आपका कोड अब अन्य रूबी कार्यान्वयन के लिए पोर्टेबल नहीं होगा।

तो, कुछ रूबी कार्यान्वयन TCO प्रदर्शन करते हैं, लेकिन अधिकांश नहीं करते हैं। उदाहरण के लिए, YARV, TCO का समर्थन करता है, हालाँकि (फिलहाल के लिए) आपको स्रोत कोड में एक पंक्ति को स्पष्ट रूप से अनसुना करना होगा और VM को फिर से जोड़ना होगा, TCO को सक्रिय करने के लिए - भविष्य के संस्करणों में यह कार्यान्वयन के बाद डिफ़ॉल्ट रूप से होने वाला है। स्थिर। तोता वर्चुअल मशीन TCO को मूल रूप से समर्थन करता है, इसलिए कार्डिनल भी आसानी से इसका समर्थन कर सकता है। CLR को TCO के लिए कुछ समर्थन है, जिसका अर्थ है कि IronRuby और Ruby.NET शायद ऐसा कर सकते हैं। रुबिनियस शायद यह भी कर सकता था।

लेकिन JRuby और XRuby TCO का समर्थन नहीं करते हैं, और वे शायद नहीं करेंगे, जब तक कि JVM खुद TCO के लिए समर्थन हासिल न कर ले। समस्या यह है: यदि आप जावा के साथ तीव्र कार्यान्वयन, और तेज़ और सहज एकीकरण चाहते हैं, तो आपको जावा के साथ स्टैक-संगत होना चाहिए और जेवीएम के स्टैक का यथासंभव उपयोग करना चाहिए। आप बहुत आसानी से ट्रम्पोलिन या स्पष्ट निरंतर-गुजर शैली के साथ TCO को लागू कर सकते हैं, लेकिन फिर आप JVM स्टैक का उपयोग नहीं कर रहे हैं, जिसका अर्थ है कि हर बार आप जावा में कॉल करना चाहते हैं या जावा से रूबी में कॉल करना चाहते हैं, आपको किसी भी तरह का प्रदर्शन करना होगा। रूपांतरण, जो धीमा है। इसलिए, एक्सक्यूबी और जेरी ने टीसीओ और निरंतरता पर गति और जावा एकीकरण के साथ जाना चुना (जो मूल रूप से एक ही समस्या है)।

यह रूबी के सभी कार्यान्वयनों पर लागू होता है जो कुछ होस्ट प्लेटफ़ॉर्म के साथ कसकर एकीकृत करना चाहते हैं जो TCO को मूल रूप से समर्थन नहीं करते हैं। उदाहरण के लिए, मुझे लगता है कि MacRuby में भी यही समस्या है।


2
मुझसे गलती हो सकती है (कृपया मुझे ऐसा बताएं), लेकिन मुझे संदेह है कि TCO को सही OO भाषाओं में कोई मतलब है, क्योंकि टेल कॉल कॉलर स्टैक फ्रेम का पुन: उपयोग करने में सक्षम होना चाहिए। चूंकि देर से बाध्यकारी के साथ, यह संकलन-समय पर ज्ञात नहीं है कि किस विधि को एक संदेश भेजने से आह्वान किया जाएगा, यह सुनिश्चित करना मुश्किल लगता है कि (शायद एक प्रकार की प्रतिक्रिया जेआईटी के साथ, या स्टैक फ्रेम का उपयोग करने के लिए संदेश के सभी कार्यान्वयनकर्ताओं को मजबूर करके एक ही आकार के, या TCO को एक ही संदेश के स्व-प्रेषितों तक सीमित करके ...)।
डेमियन पोललेट

2
यह एक शानदार प्रतिक्रिया है। वह जानकारी Google के माध्यम से आसानी से नहीं मिलती है। दिलचस्प है कि यारव इसका समर्थन करता है।
चार्ली फूल

15
डेमियन, यह पता चला है कि TCO वास्तव में OO भाषाओं के लिए आवश्यक है : प्रोजेक्टफॉरसे.सून . com/Projects/Community/blog /… देखें । स्टैक फ्रेम सामान के बारे में बहुत ज्यादा चिंता न करें: स्टैक फ्रेम को समझदारी से डिजाइन करना पूरी तरह से संभव है ताकि वे TCO के साथ अच्छी तरह से काम करें।
टोनी गर्नॉक-जोन्स

2
tonyg ने GLS की संदर्भित पोस्ट को विलुप्त होने से बचाया, इसे यहां दिखाया
फ्रैंक

मैं एक होमवर्क असाइनमेंट कर रहा हूं जिसके लिए मुझे मनमाने गहराई के नेस्टेड सरणियों के एक सेट को अलग करना होगा। स्पष्ट रूप से ऐसा करने का तरीका पुनरावर्ती है, और इसी तरह के मामलों का उपयोग ऑनलाइन (जो मुझे मिल सकता है) पुनरावृत्ति का उपयोग करें। मेरी विशेष समस्या को TCO के बिना भी उड़ा देने की बहुत संभावना नहीं है, लेकिन यह तथ्य कि मैं इसे पूरी तरह से सामान्य समाधान नहीं लिख सकता, जो मुझे पुनरावृत्ति करने से रोकता है।
आइजैक राबिनोविच

42

अद्यतन: यहाँ रूबी में TCO की अच्छी व्याख्या है: http://nithinbekal.com/posts/ruby-tco/

अद्यतन: आप tco_method रत्न भी देख सकते हैं : http://blog.tdg5.com/introducing-the-tco_method-gem/

रूबी एमआरआई (1.9, 2.0 और 2.1) में आप TCO को चालू कर सकते हैं:

RubyVM::InstructionSequence.compile_option = {
  :tailcall_optimization => true,
  :trace_instruction => false
}

रूबी 2.0 में TCO को डिफ़ॉल्ट रूप से चालू करने का प्रस्ताव था। यह उस के साथ आने वाले कुछ मुद्दों की भी व्याख्या करता है: टेल कॉल ऑप्टिमाइज़ेशन: डिफॉल्ट रूप से सक्षम करें ?.

लिंक से संक्षिप्त अंश:

आमतौर पर, पूंछ-पुनरावृत्ति अनुकूलन में एक और अनुकूलन तकनीक शामिल है - "कॉल" से "कूद" अनुवाद। मेरी राय में, इस अनुकूलन को लागू करना मुश्किल है क्योंकि रूबी की दुनिया में "पुनरावृत्ति" को पहचानना मुश्किल है।

अगला उदाहरण। fact () मेथड इन्वोकेशन इन "क्लोज़" एक "टेल कॉल" नहीं है।

def fact(n) 
  if n < 2
    1 
 else
   n * fact(n-1) 
 end 
end

यदि आप तथ्य () विधि पर टेल-कॉल ऑप्टिमाइज़ेशन का उपयोग करना चाहते हैं, तो आपको निम्नानुसार तथ्य () विधि को बदलने की आवश्यकता है (निरंतरता गुजर शैली)।

def fact(n, r) 
  if n < 2 
    r
  else
    fact(n-1, n*r)
  end
end

12

यह हो सकता है लेकिन इसकी गारंटी नहीं है:

https://bugs.ruby-lang.org/issues/1256


लिंक अब तक मर चुका है।
कराटेगॉग

@karatedog: धन्यवाद, अपडेट किया गया। हालांकि ईमानदारी से संदर्भ शायद तारीख से बाहर है, क्योंकि बग अब 5 साल का है और उसी विषय पर गतिविधि हुई है।
स्टीव जेसोप

हाँ :-) मैं सिर्फ विषय के बारे में पढ़ता हूं और मैंने देखा कि रूबी 2.0 में यह स्रोत कोड (कोई और सी स्रोत संशोधन और recompile) से सक्षम हो सकता है।
karatedog


2

यह जार्ग और अर्नेस्ट के उत्तरों पर बनाता है। मूल रूप से यह कार्यान्वयन पर निर्भर करता है।

मुझे एमआरआई पर काम करने के लिए अर्नेस्ट का जवाब नहीं मिला, लेकिन यह उल्लेखनीय है। मुझे यह उदाहरण मिला जो MRI 1.9 से 2.1 के लिए काम करता है। यह एक बहुत बड़ी संख्या को प्रिंट करना चाहिए। यदि आप TCO विकल्प को सही पर सेट नहीं करते हैं, तो आपको "स्टैक बहुत गहरी" त्रुटि मिलनी चाहिए।

source = <<-SOURCE
def fact n, acc = 1
  if n.zero?
    acc
  else
    fact n - 1, acc * n
  end
end

fact 10000
SOURCE

i_seq = RubyVM::InstructionSequence.new source, nil, nil, nil,
  tailcall_optimization: true, trace_instruction: false

#puts i_seq.disasm

begin
  value = i_seq.eval

  p value
rescue SystemStackError => e
  p e
end
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.