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

रेल्स प्रदर्शन अनुकूलन: कैसे तय करें कि कैशिंग कब सर्वोत्तम रणनीति है

प्रोग्रामिंग शब्दों में, कैशिंग भविष्य में त्वरित पुनर्प्राप्ति के लिए एक मान (या मानों) को संग्रहीत करने को संदर्भित करता है। आमतौर पर, आप इसे उन मानों के साथ करेंगे जो किसी कारण से गणना करने में धीमे हैं; उदाहरण के लिए, उन्हें पुनर्प्राप्त करने के लिए बाहरी API को हिट करने की आवश्यकता होती है, या उन्हें उत्पन्न करने के लिए बहुत सारी संख्या-क्रंचिंग शामिल होती है।

कैश्ड मान अक्सर एक अलग सर्वर पर संग्रहीत किए जाते हैं, जैसे मेमकैश्ड या रेडिस। उन्हें डिस्क पर या RAM में संग्रहीत किया जा सकता है। कोड में, हम अक्सर महंगे फंक्शन्स को कई बार कॉल करने से बचने के लिए वेरिएबल्स के अंदर डेटा 'कैश' करते हैं।

data = some_calculation() 
a(data) 
b(data) 

आपके द्वारा प्राप्त की जाने वाली सभी गति के लिए ट्रेड-ऑफ यह है कि आप पुराने डेटा का उपयोग कर रहे हैं। क्या होगा यदि कैश्ड डेटा 'स्टेल' हो जाए और अब सटीक न हों? आपको कैश को 'अमान्य' करने के लिए इसे साफ़ करना होगा।

कैशिंग के खिलाफ तर्क

जैसा कि पुरानी कहावत है, कंप्यूटर साइंस में केवल 2 कठिन समस्याएं हैं: 1. चीजों का नामकरण 2. कैश अमान्यता 3. ऑफ-बाय-वन त्रुटियां

कैश अमान्यता इतनी कठिन क्यों है? एक कैश्ड मान, अपनी प्रकृति द्वारा, एक 'असली' मान को 'छुपाता' है। हर बार जब 'असली' मान बदलता है, तो आपको (हां, आपको, प्रोग्रामर) कैश को 'अमान्य' करना याद रखना होगा ताकि यह अपडेट हो जाए।

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

  1. फाइल लोड करते समय शब्दों को गिनें।
  2. इस वर्ड-काउंट को एक वेरिएबल में सेव करें (या 'इसे कैश करें')।
  3. स्क्रीन पर वेरिएबल की सामग्री प्रदर्शित करें।

यह कार्यान्वयन बहुत तेज़ है, लेकिन कैश्ड 'वर्ड काउंट' टाइप करते समय नहीं बदलता। ऐसा करने के लिए, हमें जब भी वर्ड काउंट बदलने की उम्मीद हो तो कैश को 'अमान्य' करने की आवश्यकता है।

अब, जैसे-जैसे कीस्ट्रोक्स आते हैं, आप शब्दों (यानी, स्पेसेस) का पता लगाएंगे और वर्ड काउंटर को बढ़ाएंगे। बेशक, जब उपयोगकर्ता शब्दों को हटा रहा हो तो आप इसे घटाएंगे। आसान। हो गया। अगला टिकट।

...लेकिन रुकिए, क्या आपने याद रखा कि जब उपयोगकर्ता टेक्स्ट को क्लिपबोर्ड पर काटता है तो वर्ड काउंट को अपडेट करना है? जब वे टेक्स्ट पेस्ट करते हैं तो क्या होता है? क्या होता है जब स्पेल-चेकर एक टाइपो को दो शब्दों में विभाजित करता है?

यहां समस्या मान को अपडेट करना नहीं है, जो काफी तुच्छ है। समस्या यह है कि आपको हर एक जगह इसे अपडेट करना याद रखना होगा। इन अपडेट्स में से सिर्फ एक को भूल जाने से कैश अमान्यता की समस्याएं होती हैं, जिसका अर्थ है कि आप उपयोगकर्ता को एक स्टेल मान प्रदर्शित करेंगे।

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

कैशिंग के बिना गति

अगर हम कैशिंग को टेबल से हटा दें, तो हमारे एप्लिकेशन को तेज करना प्रदर्शन बॉटलनेक्स की पहचान और उन्हें ठीक करने के बारे में है - सिस्टम जो जितने तेज हो सकते हैं उससे धीमे हैं। हम उन्हें तीन समग्र श्रेणियों में समूहित कर सकते हैं:

  1. डेटाबेस क्वेरीज़ (या तो बहुत सारी या बहुत धीमी)
  2. व्यू रेंडरिंग
  3. एप्लिकेशन कोड (उदाहरण के लिए, भारी गणना करना)

प्रदर्शन पर काम करते समय, दो तकनीकें हैं जिन्हें आपको प्रगति करने के लिए जानना आवश्यक है: प्रोफाइलिंग और बेंचमार्किंग।

प्रोफाइलिंग

प्रोफाइलिंग है कि आप कैसे जानते हैं कि आपके ऐप में समस्याएं कहां हैं: क्या यह पेज धीमा है क्योंकि टेम्पलेट रेंडर करना धीमा है? या, यह धीमा है क्योंकि यह डेटाबेस को लाखों बार हिट कर रहा है?

रूबी ऑन रेल्स के लिए, मैं rack-mini-profiler की सिफारिश करूंगा, जो आपके ऐप के किनारे पर एक अच्छा छोटा विजेट जोड़ता है। यह आपको उस पेज को रेंडर करने में क्या लगा, इसका एक अच्छा अवलोकन देता है, जैसे कि कितने डेटाबेस क्वेरीज़ फायर हुए, उन्हें कितना समय लगा, और कितने पार्शियल्स रेंडर किए गए।

उत्पादन के लिए (प्रो-टिप: rack-mini-profiler उत्पादन में अच्छी तरह से काम करता है; बस सुनिश्चित करें कि यह केवल कुछ उपयोगकर्ताओं के लिए दिखाई देता है, जैसे एडमिन या डेवलपर्स), ऑनलाइन सेवाएं हैं, जिनमें स्काईलाइट, न्यू रेलिक, और स्काउट शामिल हैं, जो पेज प्रदर्शन की निगरानी करते हैं।

आमतौर पर उद्धृत लक्ष्य <= 100ms पेज रेंडरिंग के लिए बहुत अच्छा है, क्योंकि वास्तविक दुनिया के इंटरनेट उपयोग में उपयोगकर्ता के लिए इससे कम किसी भी चीज़ का पता लगाना मुश्किल है। आपका लक्ष्य कई कारकों के आधार पर अलग-अलग होगा। एक बिंदु पर, जब खराब प्रदर्शन वाले एक लिगेसी एप्लिकेशन पर काम कर रहा था, तो मैंने <= 1 सेकंड को लक्ष्य बनाया, जो बहुत अच्छा नहीं है लेकिन जब मैंने शुरू किया था उससे कहीं बेहतर है।

बेंचमार्किंग

एक बार जब हम पता लगा लेते हैं कि समस्या कहां है, तो हम बेंचमार्क का उपयोग यह देखने के लिए कर सकते हैं कि हमारे अनुकूलन का प्रदर्शन पर क्या (यदि कोई) प्रभाव पड़ा। व्यक्तिगत रूप से, मैं इस तरह के काम के लिए benchmark-ips जेम का उपयोग करना पसंद करता हूं, क्योंकि यह आपके कोड द्वारा किए गए अंतर को देखने का एक आसान मानव-पठनीय तरीका देता है।

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

require 'benchmark/ips' 
@a = "abc" 
@b = "def" 
Benchmark.ips do |x| 
  x.report("Concatenation") { @a + @b } 
  x.report("Interpolation") { "#{@a}#{@b}" } 
  x.compare! 
end 

और परिणाम:

Warming up -------------------------------------- 
Concatenation 316.022k i/100ms 
Interpolation 282.422k i/100ms 
Calculating ------------------------------------- 
Concatenation 10.353M (± 7.4%) i/s - 51.512M in 5.016567s 
Interpolation 6.615M (± 6.8%) i/s - 33.043M in 5.023636s 
Comparison: 
Concatenation: 10112435.3 i/s 
Interpolation: 6721867.3 i/s - 1.50x slower 

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

प्रदर्शन समस्याओं को ठीक करना

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

डेटाबेस प्रदर्शन

डेटाबेस-संबंधी प्रदर्शन मुद्दों के लिए, कुछ चीजें हैं जिन्हें देखना है। सबसे पहले, खतरनाक 'N+1 क्वेरीज़' से बचें। ऐसी स्थितियां अक्सर एक व्यू में एक संग्रह को रेंडर करने में होती हैं। उदाहरण के लिए, आपके पास 10 ब्लॉग पोस्ट वाला एक यूज़र है, और आप यूज़र और उसकी सभी पोस्ट प्रदर्शित करना चाहते हैं। एक भोला पहला प्रयास ऐसा कुछ हो सकता है:

# कंट्रोलर 
def show 
  @user = User.find(params[:id]) 
end 
# व्यू 
Name: <%= @user.name %> 
Posts: <%= @user.posts each do |post| %> 
  <div>Title: <%= post.title %></div> 
<% end %> 

ऊपर दिखाया गया दृष्टिकोण यूज़र (1 क्वेरी) प्राप्त करेगा और फिर प्रत्येक व्यक्तिगत पोस्ट के लिए एक क्वेरी फायर करेगा (N=10 क्वेरीज़), जिसके परिणामस्वरूप 11 कुल (या N+1) होंगे। सौभाग्य से, रेल्स आपके ActiveRecord क्वेरी में .includes(:post) जोड़कर इस समस्या का एक सरल समाधान प्रदान करता है। इसलिए, उपरोक्त उदाहरण में, हम बस कंट्रोलर कोड को निम्नलिखित में बदलते हैं:

def show 
  @user = User.includes(:post).find(params[:id]) 
end 

अब, हम एक डेटाबेस क्वेरी में यूज़र और उसकी सभी पोस्ट्स को प्राप्त करेंगे।

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

total = Model.all.map(&:somefield).sum 

यह डेटाबेस से सभी रिकॉर्ड्स ले रहा है, लेकिन मूल्यों का वास्तविक योग रूबी में होता है। हम डेटाबेस को हमारे लिए गणना करने देकर इसे तेज़ कर सकते हैं जैसे:

total = Model.sum(:somefield) 

शायद आपको कुछ अधिक जटिल चाहिए, जैसे दो कॉलम को गुणा करना:

total = Model.sum('columna * columnb') 

सामान्य डेटाबेस इस तरह के बुनियादी अंकगणित और साथ ही सामान्य एग्रीगेशन जैसे योग और औसत का समर्थन करते हैं, इसलिए अपने कोडबेस में map(...).sum कॉल्स पर नज़र रखें।

व्यू प्रदर्शन

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

सामान्य पेज-लोड समय के लिए, आप जांच सकते हैं कि आप किसी भी जावास्क्रिप्ट या CSS लाइब्रेरी के लिए मिनिफाइड सोर्स का उपयोग कर रहे हैं (कम से कम उत्पादन सर्वर पर)।

इसके अलावा, बड़ी संख्या में शामिल पार्शियल्स के लिए देखें। यदि आपका _widget.html.erb टेम्प्लेट प्रोसेस करने में 1ms लेता है, लेकिन आपके पेज पर 100 विजेट हैं, तो यह पहले से ही 100ms चला गया है। एक समाधान अपने UI पर पुनर्विचार करना है। एक बार में स्क्रीन पर 100 विजेट होना आमतौर पर एक शानदार उपयोगकर्ता अनुभव नहीं है, और आप किसी प्रकार के पेजिनेशन का उपयोग करने पर विचार करना चाह सकते हैं, या शायद, एक और भी अधिक कठोर UI/UX ओवरहाल।

एप्लिकेशन कोड प्रदर्शन

यदि आपका प्रदर्शन मुद्दा एप्लिकेशन कोड में ही है (यानी, डेटा का हेरफेर) व्यू या डेटाबेस लेयर्स के बजाय, तो आपके पास कुछ विकल्प हैं। एक यह देखना है कि क्या काम का कम से कम कुछ हिस्सा डेटाबेस में धकेला जा सकता है या तो क्वेरीज़ के रूप में, जैसा कि ऊपर वर्णित है, या डेटाबेस व्यूज़ के साथ, शायद scenic जेम जैसी किसी चीज़ के साथ)।

एक अन्य विकल्प 'भारी उठाने' को बैकग्राउंड जॉब में ले जाना है, हालांकि इसके लिए आपके UI में बदलाव की आवश्यकता हो सकती है ताकि यह तथ्य संभाला जा सके कि मान अब एसिंक्रोनस रूप से गणना किया जाएगा।

मुझे अभी भी कैशिंग की आवश्यकता है; अब क्या?

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

  1. रूबी केस स्टेटमेंट्स में महारत: बहुमुखी उपयोग और व्यावहारिक उदाहरण रूबी केस स्टेटमेंट्स में महारत: बहुमुखी उपयोग और व्यावहारिक उदाहरण

    जब भी आपको कुछ if / elsif स्टेटमेंट्स का उपयोग करने की आवश्यकता होती है तो आप इसके बजाय रूबी केस स्टेटमेंट का उपयोग करने पर विचार कर सकते हैं। इस पोस्ट में, आप कुछ अलग उपयोग के मामले सीखेंगे और यह वास्तव में हुड के नीचे कैसे काम करता है। नोट: अन्य प्रोग्रामिंग भाषाओं में इसे स्विच स्टेटमेंट के रूप

  1. Rails प्रदर्शन में महारत: अपने ऐप्स का निदान और त्वरण करने का सिद्ध दृष्टिकोण Rails प्रदर्शन में महारत: अपने ऐप्स का निदान और त्वरण करने का सिद्ध दृष्टिकोण

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

  1. एंड्रॉइड डिवाइस को ओवरक्लॉक करके तेज़ प्रदर्शन पाएं: सुरक्षित और प्रभावी टिप्स एंड्रॉइड डिवाइस को ओवरक्लॉक करके तेज़ प्रदर्शन पाएं: सुरक्षित और प्रभावी टिप्स

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