आज मैं प्रदर्शन में सुधार के लिए अपनी पसंदीदा तकनीकों में से एक के बारे में बात करना चाहता था। यह आसान छोटे प्रदर्शन जीत का एक स्रोत है जो अंततः जुड़ जाता है, और केवल कभी-कभी आपके आवेदन को सुलगते मलबे के ढेर तक कम कर देता है। केवल कभी-कभार ही।
इस तकनीक को "संस्मरण" कहा जाता है। $ 10 कंप्यूटर-विज्ञान शब्द के बावजूद, इसका सीधा सा मतलब है कि, हर बार जब आप किसी विधि को कॉल करते हैं तो एक ही काम करने के बजाय, आप वापसी मूल्य को एक चर में सहेजते हैं और इसके बजाय इसका उपयोग करते हैं।
यहाँ यह स्यूडोकोड में कैसा दिखता है:
def my_method
@memo = <work> if @memo is undefined
return @memo
end
और यहां आप इसे रूबी में कैसे करते हैं। यह सबसे मजबूत दृष्टिकोण है, लेकिन यह काफी क्रियात्मक है। अन्य, अधिक संक्षिप्त दृष्टिकोण हैं जिन पर हम बाद में चर्चा करेंगे।
class MyClass
def my_method
unless defined?(@my_method)
@my_method = begin
# Do your calculation, database query
# or other long-running thing here.
end
end
@my_method
end
end
ऊपर दिया गया कोड तीन काम करता है:
- यह देखने के लिए जांचता है कि क्या
@my_method
. नामक एक आवृत्ति चर है . - यदि है, तो यह कुछ काम करता है और परिणाम
@my_method
में सहेजता है । - यह
@my_method
लौटाता है
इस तथ्य से भ्रमित न हों कि हमारे पास my_method
नामक एक विधि और एक आवृत्ति चर दोनों हैं . मैं अपने वेरिएबल को कुछ भी नाम दे सकता था, लेकिन इसे याद रखने की विधि के बाद इसे नाम देना परंपरा है।
एक शॉर्टहैंड संस्करण
उपरोक्त कोड के साथ एक समस्या यह है कि यह थोड़ा बोझिल है। इस वजह से, आपको एक शॉर्टहैंड संस्करण देखने की अधिक संभावना है जो लगभग एक ही काम करता है:
class MyClass
def my_method1
@my_method1 ||= some_long_calculation
end
def my_method2
@my_method2 ||= begin
# The begin-end block lets you easily
# use multiple lines of code here.
end
end
end
ये दोनों रूबी के a ||= b
. का उपयोग करते हैं ऑपरेटर, जो a || (a = b)
, जो स्वयं के लिए कमोबेश आशुलिपि है:
# You wouldn't use return like this in real life.
# I'm just using it to express to beginners the idea
# that the conditional evaluates to whatever winds up in `a`.
if a
return a
else
a = b
return a
end
बग का स्रोत
यदि आप बहुत करीब से ध्यान दे रहे हैं तो आपने देखा होगा कि शॉर्टहैंड संस्करण मेमो वेरिएबल के अस्तित्व की जांच करने के बजाय "सत्यता" का मूल्यांकन करते हैं। यह शॉर्टहैंड संस्करण की प्रमुख सीमाओं में से एक का स्रोत है:यह कभी भी nil
को याद नहीं करता है या false
.
ऐसे बहुत से उपयोग के मामले हैं जहां यह महत्वपूर्ण नहीं है। लेकिन यह उन कष्टप्रद तथ्यों में से एक है जिसे आपको याद करते समय अपने दिमाग में रखना होगा।
तर्कों के साथ याद रखने के तरीके
अब तक हमने केवल एकल मूल्यों को याद रखने के बारे में बात की है। लेकिन कई फ़ंक्शन हर समय एक ही परिणाम नहीं देते हैं। आइए तकनीकी साक्षात्कारों के पुराने पसंदीदा पर एक नज़र डालें:फाइबोनैचि अनुक्रम।
आप रूबी में फिबोनाची अनुक्रम की पुनरावर्ती रूप से गणना कर सकते हैं जैसे:
class Fibonacci
def self.calculate(n)
return n if n == 0 || n == 1
calculate(n - 1) + calculate(n - 2)
end
end
Fibonacci.calculate(10) # => 55
इस कार्यान्वयन के साथ समस्या यह है कि यह अक्षम है। इसे साबित करने के लिए, आइए एक print
जोड़ें n
. का मान देखने के लिए कथन ।
class Fibonacci
def self.calculate(n)
print "#{ n } "
return n if n == 0 || n == 1
calculate(n - 1) + calculate(n - 2)
end
end
Fibonacci.calculate(4)
# Outputs: 4 3 2 1 0 1 2 1 0
जैसा कि आप देख सकते हैं, calculate
n
. के कई समान मानों के साथ बार-बार कॉल किया जा रहा है . वास्तव में calculate
करने के लिए कॉल की संख्या n
. के साथ तेजी से बढ़ने वाला है .
इसका एक तरीका calculate
. के परिणामों को याद रखना है . ऐसा करना हमारे द्वारा कवर किए गए अन्य संस्मरण उदाहरणों से बहुत अलग नहीं है।
class Fibonacci
def self.calculate(n)
@calculate ||= {}
@calculate[n] ||= begin
print "#{ n } "
if n == 0 || n == 1
n
else
calculate(n - 1) + calculate(n - 2)
end
end
end
end
Fibonacci.calculate(4)
# Outputs: 4 3 2 1 0
अब जबकि हमने calculate
याद कर लिया है , कॉल की संख्या अब n
. के साथ तेजी से नहीं बढ़ती है ।
Fibonacci.calculate(20)
# Outputs: 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
अमान्यता
संस्मरण कैशिंग की तरह है, सिवाय इसके कि याद किए गए परिणाम स्वचालित रूप से समाप्त नहीं होते हैं, और आमतौर पर एक बार सेट होने पर उन्हें साफ़ करने का कोई आसान तरीका नहीं होता है।
फाइबोनैचि अनुक्रम जनरेटर जैसे उपयोग के मामलों के लिए यह शायद ही मायने रखता है। Fibonacci.calculate(10)
हमेशा एक ही परिणाम लौटाएगा। लेकिन अन्य उपयोग के मामलों में यह मायने रखता है।
उदाहरण के लिए, आपको इस तरह का कोड दिखाई दे सकता है:
# Not the best idea
class User
def full_name
@full_name ||= [first_name, last_name].join(" ")
end
end
व्यक्तिगत रूप से, मैं यहां संस्मरण का उपयोग नहीं करूंगा क्योंकि यदि पहला या अंतिम नाम बदल दिया जाता है, तो पूरा नाम अपडेट नहीं किया जा सकता है।
एक जगह जहां आप थोड़ा और ढीला हो सकते हैं वह रेल नियंत्रकों के अंदर है। इस तरह का कोड देखना बहुत आम है:
class ApplicationController
def current_user
@current_user ||= User.find(...)
end
end
यह ठीक है, क्योंकि प्रत्येक वेब अनुरोध के बाद नियंत्रक उदाहरण नष्ट हो जाता है। यह संभावना नहीं है कि वर्तमान में लॉग-इन उपयोगकर्ता किसी सामान्य अनुरोध के दौरान बदल जाएगा।
एक्शनकेबल जैसे स्ट्रीमिंग कनेक्शन के साथ काम करते समय आपको अधिक सावधान रहने की आवश्यकता हो सकती है। मुझें नहीं पता। मैंने कभी इसका इस्तेमाल नहीं किया।
अति प्रयोग
अंत में मुझे ऐसा लगता है कि मुझे यह बताना चाहिए कि किसी भी चीज़ की तरह, संस्मरण को बहुत दूर ले जाना संभव है। यह एक ऐसी तकनीक है जिसे वास्तव में केवल महंगे संचालन पर लागू किया जाना चाहिए जो मेमो चर के पूरे जीवनकाल में कभी नहीं बदलेगा।