Computer >> कंप्यूटर >  >> प्रोग्रामिंग >> Ruby

रूबी आपके कोड की व्याख्या कैसे करती है, इस पर एक नज़र

रूबी मैजिक के एक नए लेख में आपका स्वागत है! इस बार हम देखेंगे कि रूबी हमारे कोड की व्याख्या कैसे करती है, और हम इस ज्ञान का उपयोग अपने लाभ के लिए कैसे कर सकते हैं। यह पोस्ट आपको यह समझने में मदद करेगी कि कोड की व्याख्या कैसे की जाती है, और यह कैसे तेज़ कोड तक ले जाने में मदद कर सकता है।

प्रतीकों के बीच एक सूक्ष्म अंतर

पिछले रूबी मैजिक लेख में रूबी में एस्केपिंग कैरेक्टर्स के बारे में लाइन ब्रेक से बचने के बारे में एक उदाहरण था।

नीचे दिए गए उदाहरण में आप देखते हैं कि कैसे दो स्ट्रिंग्स को एक स्ट्रिंग के रूप में कई पंक्तियों में जोड़ा जाता है, या तो प्लस + के साथ प्रतीक या बैकस्लैश के साथ \

"foo" +
  "bar"
=> "foobar"
 
# versus
 
"foo" \
  "bar"
=> "foobar"

ये दो उदाहरण समान दिख सकते हैं, लेकिन वे काफी अलग व्यवहार करते हैं। इन्हें कैसे पढ़ा और व्याख्या किया जाता है, इसके बीच के अंतर को जानने के लिए, आपको आमतौर पर रूबी दुभाषिया के बारे में बारीक-बारीक जानकारी होनी चाहिए। या, हम रूबी से पूछ सकते हैं कि अंतर क्या है।

निर्देश अनुक्रम

RubyVM::InstructionSequence का उपयोग करना कक्षा हम रूबी से पूछ सकते हैं कि यह हमारे द्वारा दिए गए कुछ कोड की व्याख्या कैसे करता है। यह वर्ग हमें एक टूल सेट देता है जिसका उपयोग हम रूबी के इंटर्नल की एक झलक पाने के लिए कर सकते हैं।

नीचे दिए गए उदाहरण में जो दिया गया है वह रूबी कोड है जैसा कि YARV दुभाषिया द्वारा समझा जाता है।

YARV दुभाषिया

YARV (फिर भी एक और रूबी VM) रूबी संस्करण 1.9 में पेश किया गया रूबी दुभाषिया है, जो मूल दुभाषिया की जगह लेता है:MRI (Matz's Ruby Interpreter)।

दुभाषियों का उपयोग करने वाली भाषाएं मध्यवर्ती संकलन चरण के बिना सीधे कोड निष्पादित करती हैं। इसका मतलब यह है कि रूबी पहले एक प्रोग्राम को एक अनुकूलित मशीन भाषा प्रोग्राम में संकलित नहीं करता है, जो सी, रस्ट और गो डू जैसी भाषाओं को संकलित करता है।

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

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

रूबी प्रोग्राम के सामान्य निष्पादन के दौरान हम इन निर्देशों को नहीं देखते हैं, लेकिन उन्हें देखकर हम समीक्षा कर सकते हैं कि रूबी ने हमारे कोड की सही व्याख्या की है या नहीं। InstructionSequence के साथ यह देखना संभव है कि YARV उन्हें निष्पादित करने से पहले किस प्रकार के निर्देश बनाता है।

रूबी दुभाषिया बनाने वाले सभी YARV निर्देशों को समझना आवश्यक नहीं है। अधिकांश आदेश अपने लिए बोलेंगे।

"foo" +
  "bar"
RubyVM::InstructionSequence.compile('"foo" + "bar"').to_a
# ... [:putstring, "foo"], [:putstring, "bar"] ...
 
# versus
 
"foo" \
  "bar"
RubyVM::InstructionSequence.compile('"foo" "bar"').to_a
# ... [:putstring, "foobar"] ...

वास्तविक आउटपुट में कुछ और सेटअप कमांड होते हैं जिन्हें हम बाद में देखेंगे, लेकिन यहां हम "foo" + "bar" के बीच वास्तविक अंतर देख सकते हैं। और "foo" "bar"

पूर्व दो तार बनाता है और उन्हें जोड़ता है। उत्तरार्द्ध एक स्ट्रिंग बनाता है। इसका मतलब है कि "foo" "bar" . के साथ हम "foo" + "bar" . के साथ तीन के बजाय केवल एक स्ट्रिंग बनाते हैं ।

  1       2           3
  ↓       ↓           ↓
"foo" + "bar" # => "foobar"

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

  • अधिक आवंटन:प्रत्येक स्ट्रिंग ऑब्जेक्ट को अलग से आवंटित किया जाता है।
  • अधिक मेमोरी उपयोग:प्रत्येक आवंटित स्ट्रिंग ऑब्जेक्ट मेमोरी लेता है।
  • लंबे समय तक कचरा संग्रह:प्रत्येक वस्तु, भले ही अल्पकालिक हो, कचरा संग्रहकर्ता द्वारा साफ करने में समय लगता है। अधिक आवंटन का अर्थ है अधिक कचरा संग्रहण समय।

अलग करना

एक अन्य उपयोग मामला एक तर्क समस्या डीबग कर रहा है। निम्नलिखित एक आसान गलती है, जिसके बड़े परिणाम हो सकते हैं। क्या आप अंतर देख सकते हैं?

1 + 2 * 3
# versus
(1 + 2) * 3

इस थोड़े अधिक जटिल उदाहरण में अंतर का पता लगाने में हमारी मदद करने के लिए हम रूबी का उपयोग कर सकते हैं।

इस कोड उदाहरण को अलग करके हम रूबी को उसके द्वारा निष्पादित आदेशों की अधिक पठनीय तालिका मुद्रित करने के लिए प्राप्त कर सकते हैं।

1 + 2 * 3
# => 7
puts RubyVM::InstructionSequence.compile("1 + 2 * 3").disasm
# == disasm: <RubyVM::InstructionSequence:<compiled>@<compiled>>==========
# 0000 trace            1                                               (   1)
# 0002 putobject_OP_INT2FIX_O_1_C_
# 0003 putobject        2
# 0005 putobject        3
# 0007 opt_mult         <callinfo!mid:*, argc:1, ARGS_SIMPLE>
# 0009 opt_plus         <callinfo!mid:+, argc:1, ARGS_SIMPLE>
# 0011 leave
 
# versus
 
(1 + 2) * 3
# => 9
puts RubyVM::InstructionSequence.compile("(1 + 2) * 3").disasm
# == disasm: <RubyVM::InstructionSequence:<compiled>@<compiled>>==========
# 0000 trace            1                                               (   1)
# 0002 putobject_OP_INT2FIX_O_1_C_
# 0003 putobject        2
# 0005 opt_plus         <callinfo!mid:+, argc:1, ARGS_SIMPLE>
# 0007 putobject        3
# 0009 opt_mult         <callinfo!mid:*, argc:1, ARGS_SIMPLE>
# 0011 leave

ऊपर दिया गया उदाहरण YARV निर्देशों की संख्या से थोड़ा अधिक जुड़ा हुआ है, लेकिन जिस क्रम में चीजें मुद्रित और निष्पादित की जाती हैं, उससे हम देखते हैं कि कोष्ठकों की एक जोड़ी क्या अंतर कर सकती है।

1 + 2 . के आसपास कोष्ठकों के साथ हम यह सुनिश्चित करते हैं कि गणित में संक्रियाओं के क्रम को ऊपर ले जाकर पहले जोड़ दिया जाए।

ध्यान दें कि आप वास्तव में कोष्ठक को डिस्सेप्लर आउटपुट में नहीं देखते हैं, केवल शेष कोड पर उनका प्रभाव है।

अलग करना

Disassembly आउटपुट बहुत सी ऐसी चीजें प्रिंट करता है जो शायद तुरंत समझ में न आए।

टेबल फॉर्मेट में जो प्रिंट होता है, हर लाइन एक ऑपरेशन नंबर से शुरू होती है। जिसके बाद यह ऑपरेशन का उल्लेख करता है और अंत में ऑपरेशन के तर्क का उल्लेख करता है।

संचालन का एक छोटा सा नमूना जो हमने अब तक देखा है:

  • trace - एक ट्रेस शुरू करें। अधिक जानकारी के लिए ट्रेसपॉइंट पर दस्तावेज़ देखें।
  • putobject - स्टैक पर किसी ऑब्जेक्ट को पुश करें।
  • putobject_OP_INT2FIX_O_1_C_ - इंटीजर को पुश करें 1 ढेर पर। अनुकूलित संचालन। (0 और 1 अनुकूलित हैं।)
  • putstring - स्टैक पर एक स्ट्रिंग पुश करें।
  • opt_plus - अतिरिक्त संचालन (आंतरिक रूप से अनुकूलित)।
  • opt_mult - गुणा ऑपरेशन (आंतरिक रूप से अनुकूलित)।
  • leave - वर्तमान कोड संदर्भ को छोड़ दें।

अब जब हम जानते हैं कि रूबी दुभाषिया हमारे डेवलपर के अनुकूल और पठनीय रूबी कोड को YARV निर्देशों में कैसे परिवर्तित करता है, तो हम इसका उपयोग अपने अनुप्रयोगों को अनुकूलित करने के लिए कर सकते हैं।

RubyVM::InstructionSequence में संपूर्ण विधियों और यहां तक ​​कि संपूर्ण फ़ाइलों को पास करना संभव है ।

puts RubyVM::InstructionSequence.disasm(method(:foo))
puts RubyVM::InstructionSequence.compile_file("/tmp/hello.rb").disasm

पता लगाएँ कि कुछ कोड क्यों काम करता है और दूसरा क्यों नहीं। जानें कि क्यों कुछ प्रतीक कोड को दूसरों की तुलना में अलग व्यवहार करते हैं। शैतान विवरण में है, और यह जानना अच्छा है कि आपका रूबी कोड आपके ऐप में कैसा व्यवहार कर रहा है और यदि आप इसे किसी भी तरह से अनुकूलित कर सकते हैं।

अनुकूलन

दुभाषिया स्तर पर अपना कोड देखने और इसके लिए अनुकूलित करने में सक्षम होने के अलावा, आप InstructionSequence का उपयोग कर सकते हैं अपने कोड को और भी अधिक अनुकूलित करने के लिए।

InstructionSequence के साथ , रूबी के अंतर्निहित प्रदर्शन अनुकूलन के साथ कुछ निर्देशों को अनुकूलित करना संभव है। उपलब्ध अनुकूलन की पूरी सूची RubyVM::InstructionSequence.compile_option = में उपलब्ध है। विधि दस्तावेज़ीकरण।

इनमें से एक अनुकूलन है टेल कॉल ऑप्टिमाइज़ेशन

RubyVM::InstructionSequence.compile विधि इस अनुकूलन को सक्षम करने के विकल्पों को इस प्रकार स्वीकार करती है:

some_code = <<-EOS
def fact(n, acc=1)
  return acc if n <= 1
  fact(n-1, n*acc)
end
EOS
puts RubyVM::InstructionSequence.compile(some_code, nil, nil, nil, tailcall_optimization: true, trace_instruction: false).disasm
RubyVM::InstructionSequence.compile(some_code, nil, nil, nil, tailcall_optimization: true, trace_instruction: false).eval

आप RubyVM::InstructionSequence.compile_option = के साथ अपने सभी कोड के लिए इस अनुकूलन को चालू भी कर सकते हैं। . बस अपने किसी अन्य कोड से पहले इसे लोड करना सुनिश्चित करें।

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

रूबी में टेल कॉल ऑप्टिमाइज़ेशन कैसे काम करता है, इस बारे में अधिक जानकारी के लिए इन लेखों को देखें:रूबी में टेल कॉल ऑप्टिमाइज़ेशन और रूबी में टेल कॉल ऑप्टिमाइज़ेशन:बैकग्राउंड।

निष्कर्ष

रूबी आपके कोड की व्याख्या RubyVM::InstructionSequence के साथ कैसे करती है, इसके बारे में और जानें और देखें कि आपका कोड वास्तव में क्या कर रहा है ताकि आप इसे और अधिक प्रदर्शनकारी बना सकें।

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

यह रूबी में कोड संकलन के लिए हमारे संक्षिप्त परिचय को समाप्त करता है। हमें यह जानना अच्छा लगेगा कि आपको यह लेख कैसा लगा, यदि इसके बारे में आपके कोई प्रश्न हैं, और आप आगे क्या पढ़ना चाहते हैं, तो हमें @AppSignal पर बताना सुनिश्चित करें।


  1. मौलिक OOP सिद्धांतों के साथ अपने रूबी कोड को नाटकीय रूप से कैसे सुधारें

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

  1. अपने रूबी तरीकों की जासूसी कैसे करें

    रूबी में एक अंतर्निहित अनुरेखण प्रणाली है जिसे आप TracePoint . का उपयोग करके एक्सेस कर सकते हैं कक्षा। कुछ चीज़ें जिन्हें आप ट्रेस कर सकते हैं, वे हैं मेथड कॉल, नए थ्रेड और अपवाद। आप इसका उपयोग क्यों करना चाहेंगे? ठीक है, यदि आप किसी निश्चित विधि के निष्पादन का पता लगाना चाहते हैं तो यह उपयोगी हो

  1. अपने रूबी कार्यक्रमों को कैसे डिबग और ठीक करें

    आपका प्रोग्राम कितनी बार ठीक वही करता है जो आप पहली बार चाहते हैं? कई बार हमारे प्रोग्राम हमारी अपेक्षा के अनुरूप काम नहीं करते हैं, इसलिए हमें रूबी को डीबग करना की कला का उपयोग करना पड़ता है। क्यों पता लगाने में हमारी मदद करने के लिए। आप निम्न त्रुटि संदेश से परिचित हो सकते हैं: undefined method