रूबी का प्रदर्शन संस्करण दर संस्करण बहुत सुधर रहा है… और रूबी विकास टीम रूबी को और भी तेज़ बनाने के लिए हर संभव प्रयास कर रही है!
इन प्रयासों में से एक 3×3 प्रोजेक्ट है।
लक्ष्य?
रूबी 3.0, रूबी 2.0 से 3 गुना तेज़ होगा।
इस प्रोजेक्ट का हिस्सा नया MJIT कंपाइलर है, जो इस लेख का विषय है।
MJIT समझाया गया
MJIT का अर्थ है "मेथड बेस्ड जस्ट-इन-टाइम कंपाइलर"।
इसका क्या मतलब है?
रूबी आपके कोड को YARV निर्देशों में संकलित करता है, ये निर्देश रूबी वर्चुअल मशीन द्वारा चलाए जाते हैं।
JIT इसमें एक और परत जोड़ता है।
यह अक्सर उपयोग किए जाने वाले निर्देशों को बाइनरी कोड में संकलित करेगा।
परिणाम एक अनुकूलित बाइनरी है जो आपके कोड को तेज़ी से चलाता है।
यह कैसे काम करता है
आइए MJIT के काम करने के तरीके का पता लगाएं ताकि इसे बेहतर ढंग से समझा जा सके।
आप रूबी 2.6 और --jit विकल्प के साथ JIT को सक्षम कर सकते हैं।
इस तरह:
ruby --jit app.rb
रूबी 2.6 JIT-विशिष्ट विकल्पों के एक सेट के साथ आता है जो हमें यह पता लगाने में मदद करेंगे कि यह वास्तव में कैसे काम करता है। आप ruby --help चलाकर इन विकल्पों को देख सकते हैं।
विकल्पों की सूची यहां दी गई है
- --jit-wait
- --jit-verbose
- --jit-save-temps
- --jit-max-cache
- --jit-min-calls
यह वर्बोज़ विकल्प एक अच्छी शुरुआत की तरह लगता है!
हम --jit-wait का भी उपयोग करने जा रहे हैं, यह रूबी को JIT कोड के संकलन के पूरा होने तक प्रतीक्षा कराता है।
सामान्य ऑपरेशन के दौरान JIT एक वर्कर थ्रेड में कोड संकलित करता है और इसके खत्म होने का इंतजार नहीं करता।
यहां वह कमांड है जिसे आप इसे टेस्ट करने के लिए चला सकते हैं:
ruby --disable-gems --jit --jit-verbose=1 --jit-wait -e "4.times { 123 }" यह प्रिंट करता है:
Successful MJIT finish
खैर, यह बहुत दिलचस्प नहीं है, है ना?
JIT कुछ नहीं कर रहा है।
क्यों?
क्योंकि डिफ़ॉल्ट रूप से, JIT केवल तभी कार्रवाई में आता है जब कोई मेथड 5 बार कॉल किया जाता है (jit-min-calls) या अधिक।
यदि हम इसे चलाते हैं:
ruby --disable-gems --jit --jit-verbose=1 --jit-wait -e "5.times { 123 }" अब हमें कुछ दिलचस्प मिलता है:
JIT success (32.1ms): block in <main>@-e:1 -> /tmp/_ruby_mjit_p13921u0.c
यह क्या कहता है?
JIT ने एक ब्लॉक संकलित किया क्योंकि हमने इसे 5 बार कॉल किया, यह आपको बताता है:
- संकलन में कितना समय लगा (
32.1ms), - ठीक क्या संकलित किया गया (
block in <main>) - वह फाइल जो उत्पन्न हुई थी (
/tmp/_ruby_mjit_p13921u0.c) इस संकलन के स्रोत के रूप में
यह फाइल C स्रोत कोड है जिसे एक ऑब्जेक्ट फाइल (.o) में संकलित किया जाता है और फिर एक साझा लाइब्रेरी फाइल (.so) में।
यदि आप --jit-save-temps विकल्प जोड़ते हैं तो आप इन फाइलों तक पहुंच प्राप्त कर सकते हैं।
यहां एक उदाहरण है:

यह JIT कैसे काम करता है की मेरी वर्तमान समझ है:
- मेथड कॉल्स गिनें
- जब कोई मेथड 5 बार कॉल किया जाता है (डिफ़ॉल्ट
jit-min-callsके लिए) तो JIT ट्रिगर होता है - एक C फाइल बनाई जाती है जिसमें इस मेथड के लिए निर्देश होते हैं (ये YARV निर्देश हैं, लेकिन इनलाइन)
- संकलन पृष्ठभूमि में होता है (जब तक
--jit-waitन हो) GCC जैसे नियमित C कंपाइलर का उपयोग करके - जब संकलन पूरा हो जाता है तो परिणामी साझा लाइब्रेरी फाइल का उपयोग तब किया जाता है जब यह मेथड कॉल किया जाता है
आइए देखें कि यह कितना प्रभावी है।
MJIT का परीक्षण: क्या यह वास्तव में तेज़ है?
MJIT का लक्ष्य रूबी को तेज़ बनाना है।
अभी यह उसमें कितना अच्छा कर रहा है?
आइए पता करें!
पहले, माइक्रोबेंचमार्क्स:
| बेंचमार्क | परिणाम (रूबी 2.6 बिना JIT की तुलना में) |
|---|---|
| while | 8x तेज़ |
| स्ट्रिंग जोड़ के साथ while | 10% तेज़ |
| गुणा के साथ while (पूर्णांक) | 4x तेज़ |
| गुणा के साथ while (Bignum) | 20% धीमा |
| स्ट्रिंग अपकेस | 10% तेज़ |
| स्ट्रिंग मैच | 2% धीमा |
| स्ट्रिंग मैच? | 10% तेज़ |
| 10k यादृच्छिक संख्याओं के साथ सरणी | 20% तेज़ |
ऐसा लगता है कि प्रदर्शन हर जगह है, लेकिन हम इससे कुछ निष्कर्ष निकाल सकते हैं…
MJIT को लूप्स वास्तव में पसंद हैं!
लेकिन यह अधिक जटिल एप्लिकेशन के साथ कैसा प्रदर्शन करता है?
आइए एक साधारण सिनात्रा ऐप के साथ प्रयास करें:
require 'sinatra' get '/' do "apples, oranges & bananas" end
यह ज्यादा नहीं लग सकता है, लेकिन कोड का यह छोटा सा हिस्सा 500 से अधिक विभिन्न मेथड्स चलाता है। JIT को काम देने के लिए पर्याप्त!
सटीक होने के लिए, यह सिनात्रा 2.0.4 है जिसमें थिन 1.7.2 है।
आप इस कमांड (अपाचे बेंच) के साथ बेंचमार्क चला सकते हैं:
ab -c 20 -t 10 https://localhost:4567/
ये परिणाम हैं:

आप इनसे बता सकते हैं कि रूबी 2.6, 2.5 से तेज़ है, लेकिन JIT को सक्षम करने से सिनात्रा 11% धीमा हो जाता है!
क्यों?
मुझे नहीं पता, यह JIT द्वारा पेश किए गए ओवरहेड के कारण हो सकता है, या क्योंकि कोड अच्छी तरह से अनुकूलित नहीं है।
C प्रोफाइलर (कॉलग्रिंड) के साथ मेरा परीक्षण बताता है कि JIT अनुकूलित कोड का उपयोग (हमने पहले खोजी गई संकलित C फाइलें) सिनात्रा के लिए बहुत कम (2% से कम) है, लेकिन यह while स्टेटमेंट के लिए बहुत अधिक (24.22%) है जिसे 8x स्पीड बूस्ट मिलता है।
JIT के साथ while बेंचमार्क के परिणाम:

JIT के साथ सिनात्रा बेंचमार्क के परिणाम:

यह कारण का हिस्सा हो सकता है, मैं कंपाइलर विशेषज्ञ नहीं हूं इसलिए मैं इससे कोई निष्कर्ष नहीं निकाल सकता।
सारांश
MJIT रूबी 2.6 में उपलब्ध एक "जस्ट-इन-टाइम कंपाइलर" है, इसे --jit फ्लैग से सक्षम किया जा सकता है। MJIT आशाजनक है और कुछ छोटे प्रोग्राम्स को तेज़ कर सकता है, लेकिन अभी भी बहुत काम करना बाकी है!
अगर आपको यह लेख पसंद आया तो इसे अपने रूबी दोस्तों के साथ शेयर करना न भूलें 🙂
पढ़ने के लिए धन्यवाद।