रूबी मैजिक के एक नए लेख में आपका स्वागत है! इस बार हम देखेंगे कि रूबी हमारे कोड की व्याख्या कैसे करती है, और हम इस ज्ञान का अपने लाभ के लिए कैसे उपयोग कर सकते हैं। यह पोस्ट आपको यह समझने में मदद करेगी कि कोड की व्याख्या कैसे की जाती है, और यह कैसे तेज़ कोड की ओर ले जा सकता है।
प्रतीकों के बीच एक सूक्ष्म अंतर
रूबी में एस्केपिंग कैरेक्टर पर पिछले रूबी मैजिक लेख में लाइन ब्रेक को एस्केप करने के बारे में एक उदाहरण था।
नीचे दिए गए उदाहरण में आप देखते हैं कि दो स्ट्रिंग्स को या तो प्लस + प्रतीक या बैकस्लैश \ के साथ कई लाइनों में एक स्ट्रिंग के रूप में जोड़ा जाता है।
"foo" + "bar" => "foobar" # बनाम
"foo" \ "bar" => "foobar"
ये दो उदाहरण समान दिख सकते हैं, लेकिन वे काफी अलग व्यवहार करते हैं। यह जानने के लिए कि ये कैसे पढ़े और व्याख्या किए जाते हैं, आपको आमतौर पर रूबी इंटरप्रेटर के बारे में बारीक विवरण जानने की आवश्यकता होगी। या, हम बस रूबी से पूछ सकते हैं कि अंतर क्या है।
InstructionSequence
RubyVM::InstructionSequence क्लास का उपयोग करके हम रूबी से पूछ सकते हैं कि यह हमारे द्वारा दिए गए कुछ कोड की व्याख्या कैसे करता है। यह क्लास हमें एक टूल सेट देती है जिसका उपयोग हम रूबी के आंतरिक भाग की झलक पाने के लिए कर सकते हैं।
नीचे दिए गए उदाहरण में जो लौटाया जाता है वह रूबी कोड है जैसा कि YARV इंटरप्रेटर द्वारा समझा जाता है।
YARV इंटरप्रेटर
YARV (येट अनदर रूबी VM) रूबी इंटरप्रेटर है जिसे रूबी संस्करण 1.9 में पेश किया गया था, जिसने मूल इंटरप्रेटर: MRI (मैट्ज़ रूबी इंटरप्रेटर) को प्रतिस्थापित किया।
इंटरप्रेटर का उपयोग करने वाली भाषाएं इंटरमीडिएट संकलन चरण के बिना सीधे कोड निष्पादित करती हैं। इसका मतलब है कि रूबी पहले किसी प्रोग्राम को ऑप्टिमाइज़्ड मशीन लैंग्वेज प्रोग्राम में संकलित नहीं करती, जैसा कि संकलित भाषाएं जैसे C, Rust और Go करती हैं।
रूबी में, एक प्रोग्राम को पहले रूबी VM के लिए एक निर्देश सेट में अनुवादित किया जाता है, और फिर तुरंत बाद निष्पादित किया जाता है। ये निर्देश आपके रूबी कोड और रूबी VM में निष्पादित किए जा रहे कोड के बीच एक मध्यवर्ती चरण हैं।
ये निर्देश रूबी VM के लिए रूबी कोड को समझना आसान बनाते हैं बिना सिंटैक्स-विशिष्ट व्याख्या से निपटे। यह इन निर्देशों को बनाते समय संभाला जाता है। निर्देश अनुक्रम अनुकूलित ऑपरेशन हैं जो व्याख्या किए गए कोड का प्रतिनिधित्व करते हैं।
रूबी प्रोग्राम के सामान्य निष्पादन के दौरान हम इन निर्देशों को नहीं देखते, लेकिन उन्हें देखकर हम समीक्षा कर सकते हैं कि क्या रूबी ने हमारे कोड की सही व्याख्या की है। InstructionSequence के साथ YARV द्वारा उन्हें निष्पादित करने से पहले किस तरह के निर्देश बनाए जाते हैं, यह देखना संभव है।
YARV निर्देशों को समझना आवश्यक नहीं है जो रूबी इंटरप्रेटर को बनाते हैं। अधिकांश कमांड स्वयं के लिए बोलेंगे।
"foo" + "bar"
RubyVM::InstructionSequence.compile('"foo" + "bar"').to_a
# ...
[:putstring, "foo"], [:putstring, "bar"]
# ...
# बनाम
"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 # बनाम (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
# बनाम
(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 के चारों ओर पैरेंथेसिस के साथ हम सुनिश्चित करते हैं कि जोड़ पहले किया जाता है, इसे गणित में ऑपरेशन के क्रम में ऊपर ले जाकर।
ध्यान दें कि आप डिसअसेंबली आउटपुट में वास्तव में पैरेंथेसिस नहीं देखते, केवल उनका शेष कोड पर प्रभाव।
डिसअसेंबली
डिसअसेंबली आउटपुट बहुत सी चीजें प्रिंट करता है जो तुरंत समझ में नहीं आ सकतीं।
प्रिंट किए गए तालिका प्रारूप में, प्रत्येक लाइन एक ऑपरेशन नंबर से शुरू होती है। जिसके बाद यह ऑपरेशन और अंत में ऑपरेशन के आर्गुमेंट का उल्लेख करता है।
अब तक देखे गए ऑपरेशन का एक छोटा नमूना:
trace- ट्रेस शुरू करें। अधिक जानकारी के लिए TracePoint पर डॉक्स देखें।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 के साथ रूबी द्वारा आपके कोड की व्याख्या कैसे की जाती है, इसके बारे में अधिक जानें और देखें कि आपका कोड वास्तव में क्या कर रहा है ताकि आप इसे अधिक प्रदर्शनशील बना सकें।
InstructionSequence का यह परिचय रूबी के हुड के नीचे कैसे काम करता है, इसके बारे में अधिक जानने का एक मजेदार तरीका भी हो सकता है। कौन जानता है? आप रूबी के कुछ कोड पर काम करने में भी रुचि ले सकते हैं।
यह रूबी में कोड संकलन के लिए हमारे संक्षिप्त परिचय का समापन करता है। हमें यह जानकर खुशी होगी कि आपको यह लेख कैसा लगा, यदि आपके कोई प्रश्न हैं, और आप आगे क्या पढ़ना चाहेंगे, तो @AppSignal पर हमें बताना सुनिश्चित करें।