LuaJIT और Standard Lua में क्या अंतर है?

API7.ai

September 23, 2022

OpenResty (NGINX + Lua)

आइए LuaJIT के बारे में जानें, जो OpenResty का एक और मुख्य आधार है, और मैं आज के पोस्ट का मुख्य भाग Lua और LuaJIT के कुछ आवश्यक और कम ज्ञात पहलुओं पर छोड़ दूंगा।

आप Lua के मूल सिद्धांतों को खोज इंजन या Lua पुस्तकों के माध्यम से और अधिक जान सकते हैं, और मैं Lua लेखक की पुस्तक Programming in Lua की सिफारिश करता हूं।

बेशक, OpenResty में सही LuaJIT कोड लिखने की सीमा अधिक नहीं है। फिर भी, कुशल LuaJIT कोड लिखना आसान नहीं है, और मैं यहां मुख्य तत्वों को OpenResty प्रदर्शन अनुकूलन अनुभाग में बाद में विस्तार से कवर करूंगा।

आइए देखें कि LuaJIT समग्र OpenResty आर्किटेक्चर में कहां फिट बैठता है।

OpenResty Architecture

जैसा कि पहले बताया गया है, OpenResty Worker प्रक्रियाएं Master प्रक्रिया को फोर्क करके प्राप्त की जाती हैं। Master प्रक्रिया में LuaJIT वर्चुअल मशीन भी फोर्क की जाती है। एक ही Worker के भीतर सभी Worker प्रक्रियाएं इस LuaJIT वर्चुअल मशीन को साझा करती हैं, और Lua कोड का निष्पादन इस वर्चुअल मशीन में किया जाता है।

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

मानक Lua और LuaJIT के बीच संबंध

आइए पहले आवश्यक चीजों को स्पष्ट कर लें।

मानक Lua और LuaJIT दो अलग-अलग चीजें हैं। LuaJIT केवल Lua 5.1 सिंटैक्स के साथ संगत है।

मानक Lua का नवीनतम संस्करण अब 5.4.4 है, और LuaJIT का नवीनतम संस्करण 2.1.0-beta3 है। कुछ साल पहले के OpenResty के पुराने संस्करणों में, आप संकलन करते समय निष्पादन वातावरण के रूप में या तो मानक Lua VM या LuaJIT VM का उपयोग करने का विकल्प चुन सकते थे, लेकिन अब मानक Lua के लिए समर्थन हटा दिया गया है, और केवल LuaJIT का समर्थन किया जाता है।

LuaJIT सिंटैक्स Lua 5.1 के साथ संगत है, और Lua 5.2 और 5.3 के लिए वैकल्पिक समर्थन प्रदान करता है। इसलिए हमें पहले Lua 5.1 सिंटैक्स सीखना चाहिए और उस पर LuaJIT की विशेषताओं को सीखना चाहिए। पिछले लेख में, मैंने आपको Lua के मूल सिंटैक्स से परिचित कराया था। आज मैं केवल Lua की कुछ अनूठी विशेषताओं का उल्लेख करूंगा।

यह ध्यान देने योग्य है कि OpenResty सीधे आधिकारिक LuaJIT संस्करण 2.1.0-beta3 का उपयोग नहीं करता है, बल्कि इसके फोर्क के साथ इसे विस्तारित करता है: openresty-luajit2

ये अनूठे API OpenResty के वास्तविक विकास के दौरान प्रदर्शन कारणों से जोड़े गए थे। इसलिए, हम जिस LuaJIT का उल्लेख करते हैं, वह OpenResty द्वारा स्वयं बनाए रखी गई LuaJIT शाखा को संदर्भित करता है।

LuaJIT क्यों?

LuaJIT और Lua के बीच के संबंध के बारे में इतनी बात करने के बाद, आप सोच रहे होंगे कि आप सीधे Lua का उपयोग क्यों नहीं करते, बल्कि LuaJIT का उपयोग करते हैं। वास्तव में, मुख्य कारण LuaJIT का प्रदर्शन लाभ है।

Lua कोड को सीधे व्याख्या नहीं किया जाता है, बल्कि Lua कंपाइलर द्वारा Byte Code में संकलित किया जाता है और फिर Lua वर्चुअल मशीन द्वारा निष्पादित किया जाता है।

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

अंतर यह है कि LuaJIT इंटरप्रेटर बाइटकोड को निष्पादित करते समय कुछ रनटाइम सांख्यिकी रिकॉर्ड करता है, जैसे कि प्रत्येक Lua फ़ंक्शन कॉल एंट्री को वास्तव में कितनी बार चलाया जाता है और प्रत्येक Lua लूप को वास्तव में कितनी बार निष्पादित किया जाता है। जब ये गणना एक यादृच्छिक सीमा से अधिक हो जाती है, तो संबंधित Lua फ़ंक्शन एंट्री या Lua लूप को पर्याप्त रूप से गर्म माना जाता है और JIT कंपाइलर को काम करने के लिए ट्रिगर किया जाता है।

JIT कंपाइलर संबंधित Lua कोड पथ को संकलित करने का प्रयास करता है, जो गर्म फ़ंक्शन की एंट्री या गर्म लूप के स्थान से शुरू होता है। संकलन प्रक्रिया LuaJIT बाइटकोड को LuaJIT के स्वयं परिभाषित IR (इंटरमीडिएट रिप्रेजेंटेशन) में परिवर्तित करती है और फिर लक्ष्य आर्किटेक्चर के लिए मशीन कोड उत्पन्न करती है।

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

Lua की विशेष विशेषताएं

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

1. इंडेक्स 1 से शुरू होता है

Lua एकमात्र प्रोग्रामिंग भाषा है जिसे मैं जानता हूं जो 1 से इंडेक्स शुरू करती है। यह, जबकि गैर-प्रोग्रामर पृष्ठभूमि वाले लोगों द्वारा बेहतर समझा जाता है, प्रोग्राम बग के लिए प्रवण है। यहां एक उदाहरण है।

$ resty -e 't={100}; ngx.say(t[0])'

आप उम्मीद कर सकते हैं कि प्रोग्राम 100 प्रिंट करेगा या यह कहकर त्रुटि रिपोर्ट करेगा कि इंडेक्स 0 मौजूद नहीं है। लेकिन आश्चर्यजनक रूप से, कुछ भी प्रिंट नहीं होता है, और कोई त्रुटि रिपोर्ट नहीं की जाती है। तो आइए type कमांड जोड़ें और देखें कि आउटपुट क्या है।

$ resty -e 't={100};ngx.say(type(t[0]))' nil

यह nil मान निकलता है। वास्तव में, OpenResty में, nil मानों का निर्धारण और संचालन भी एक भ्रमित करने वाला बिंदु है, इसलिए हम OpenResty के बारे में बात करते समय इसके बारे में और अधिक बात करेंगे।

2. स्ट्रिंग्स को जोड़ने के लिए .. का उपयोग करें

अधिकांश भाषाओं के विपरीत जो + का उपयोग करती हैं, Lua स्ट्रिंग्स को जोड़ने के लिए दो डॉट मार्क्स का उपयोग करती है।

$ resty -e "ngx.say('hello' .. ', world')" hello, world

वास्तविक परियोजना विकास में, हम आमतौर पर कई विकास भाषाओं का उपयोग करते हैं, और Lua का असामान्य डिज़ाइन हमेशा डेवलपर्स को सोचने पर मजबूर करेगा जब स्ट्रिंग स्प्लिसिंग थोड़ा भ्रमित करने वाली होती है।

3. टेबल एकमात्र डेटा संरचना है

Python के विपरीत, जो बिल्ट-इन डेटा संरचनाओं से समृद्ध है, Lua में केवल एक डेटा संरचना है, table, जिसमें सरणियाँ और हैश टेबल शामिल हो सकते हैं।

local color = {first = "red", "blue", third = "green", "yellow"} print(color["first"]) --> output: red print(color[1]) --> output: blue print(color["third"]) --> output: green print(color[2]) --> output: yellow print(color[3]) --> output: nil

यदि आप स्पष्ट रूप से एक की-वैल्यू पेयर के रूप में मान निर्दिष्ट नहीं करते हैं, तो टेबल डिफ़ॉल्ट रूप से एक संख्या को इंडेक्स के रूप में उपयोग करती है, जो 1 से शुरू होती है। इसलिए color[1] blue है।

इसके अलावा, टेबल में सही लंबाई प्राप्त करना मुश्किल है, तो आइए इन उदाहरणों को देखें।

local t1 = { 1, 2, 3 } print("Test1 " .. table.getn(t1)) local t2 = { 1, a = 2, 3 } print("Test2 " .. table.getn(t2)) local t3 = { 1, nil } print("Test3 " .. table.getn(t3)) local t4 = { 1, nil, 2 } print("Test4 " .. table.getn(t4))

परिणाम:

Test1 3 Test2 2 Test3 1 Test4

जैसा कि आप देख सकते हैं, पहले टेस्ट केस को छोड़कर जो 3 की लंबाई लौटाता है, बाद के टेस्ट हमारी अपेक्षाओं से बाहर हैं। वास्तव में, Lua में टेबल लंबाई प्राप्त करने के लिए, यह ध्यान रखना महत्वपूर्ण है कि सही मान केवल तब लौटाया जाता है जब टेबल एक सीक्वेंस हो।

तो सीक्वेंस क्या है? सबसे पहले, सीक्वेंस एक सरणी का उपसमुच्चय है। यानी, टेबल के तत्वों को एक सकारात्मक पूर्णांक इंडेक्स के साथ एक्सेस किया जा सकता है और कोई की-वैल्यू पेयर नहीं होते हैं। उपरोक्त कोड में, t2 को छोड़कर सभी टेबल सरणियाँ हैं।

दूसरा, सीक्वेंस में कोई छेद नहीं होता है, यानी nil। इन दो बिंदुओं को मिलाकर, उपरोक्त टेबल, t1, एक सीक्वेंस है, जबकि t3 और t4 सरणियाँ हैं लेकिन सीक्वेंस नहीं हैं।

इस बिंदु तक, आपके पास अभी भी एक प्रश्न हो सकता है, क्यों t4 की लंबाई 1 होगी? ऐसा इसलिए है क्योंकि जब nil का सामना होता है, तो लंबाई प्राप्त करने का तर्क आगे नहीं चलता है बल्कि सीधे लौट जाता है।

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

4. सभी वेरिएबल डिफ़ॉल्ट रूप से ग्लोबल होते हैं

मैं इस बात पर जोर देना चाहूंगा कि जब तक आप पूरी तरह से निश्चित न हों, आपको हमेशा नए वेरिएबल को local वेरिएबल के रूप में घोषित करना चाहिए।

local s = 'hello'

ऐसा इसलिए है क्योंकि, Lua में, वेरिएबल डिफ़ॉल्ट रूप से ग्लोबल होते हैं और _G नामक एक टेबल में रखे जाते हैं। जो वेरिएबल लोकल नहीं होते हैं, उन्हें ग्लोबल टेबल में खोजा जाता है, जो एक महंगा ऑपरेशन है। वेरिएबल नामों की गलत वर्तनी के कारण बग हो सकते हैं जिन्हें पहचानना और ठीक करना मुश्किल होता है।

इसलिए, OpenResty में, मैं दृढ़ता से अनुशंसा करता हूं कि आप हमेशा local का उपयोग करके वेरिएबल घोषित करें, यहां तक कि जब आप एक मॉड्यूल की आवश्यकता होती है।

-- अनुशंसित local xxx = require('xxx') -- टालें require('xxx')

LuaJIT

Lua की इन चार विशेष विशेषताओं को ध्यान में रखते हुए, आइए अब LuaJIT पर चलते हैं।

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

local ffi = require("ffi") ffi.cdef[[ int printf(const char *fmt, ...); ]] ffi.C.printf("Hello %s!", "world")

कुछ ही लाइनों में, आप Lua से सीधे C के printf फ़ंक्शन को कॉल कर सकते हैं और Hello world! प्रिंट कर सकते हैं। आप इसे चलाने के लिए resty कमांड का उपयोग कर सकते हैं और देख सकते हैं कि यह काम करता है या नहीं।

इसी तरह, हम FFI का उपयोग करके NGINX और OpenSSL के C फ़ंक्शन को कॉल करके और भी बहुत कुछ कर सकते हैं। FFI दृष्टिकोण पारंपरिक Lua/C API दृष्टिकोण की तुलना में बेहतर प्रदर्शन करता है, यही कारण है कि lua-resty-core प्रोजेक्ट मौजूद है। अगले भाग में, हम FFI और lua-resty-core के बारे में बात करेंगे।

इसके अलावा, प्रदर्शन कारणों से, LuaJIT ने टेबल फ़ंक्शन को विस्तारित किया है: table.new और table.clear, दो आवश्यक प्रदर्शन अनुकूलन फ़ंक्शन जो OpenResty के lua-resty लाइब्रेरी में अक्सर उपयोग किए जाते हैं। हालांकि, कुछ डेवलपर्स इनसे परिचित हैं, क्योंकि दस्तावेज़ीकरण गहन है और कोई नमूना कोड नहीं है। हम इन्हें प्रदर्शन अनुकूलन अनुभाग के लिए बचा लेंगे।

सारांश

आइए आज की सामग्री की समीक्षा करें।

OpenResty प्रदर्शन कारणों से मानक Lua के बजाय LuaJIT को चुनता है और अपनी LuaJIT शाखा को बनाए रखता है। LuaJIT Lua 5.1 सिंटैक्स पर आधारित है और Lua 5.2 और Lua 5.3 सिंटैक्स के कुछ हिस्सों के साथ चयनात्मक रूप से संगत है। जहां तक Lua सिंटैक्स की बात है जिसे आपको मास्टर करने की आवश्यकता है, इसमें इंडेक्स, स्ट्रिंग स्प्लिसिंग, डेटा संरचनाओं और वेरिएबल्स में इसकी विशिष्ट विशेषताएं हैं, जिन पर आपको कोड लिखते समय विशेष ध्यान देना चाहिए।

क्या आप Lua और LuaJIT सीखते समय किसी भी कठिनाई का सामना कर चुके हैं? अपने विचार हमारे साथ साझा करने के लिए स्वतंत्र महसूस करें, और मैंने एक पोस्ट लिखी है जिसमें मैंने अपने सामने आई कठिनाइयों को साझा किया है। आप इस पोस्ट को अपने सहयोगियों और दोस्तों के साथ साझा करने के लिए भी स्वागत कर रहे हैं ताकि सीखने और प्रगति करने में मदद मिल सके।