“प्रोग्रामर अपने कार्यक्रमों की गति के बारे में चिंता करने में बड़ी मात्रा में खर्च करते हैं, और दक्षता हासिल करने के प्रयासों में अक्सर डिबग करने और उन्हें समर्थन करने की क्षमता पर एक नाटकीय नकारात्मक प्रभाव पड़ता है। 97% मामलों में, छोटे अनुकूलन के बारे में भूलना आवश्यक है। समयपूर्व अनुकूलन सभी बुराई की जड़ है! लेकिन हमें उन 3% की दृष्टि नहीं खोनी चाहिए जहां यह वास्तव में महत्वपूर्ण है! "
डोनाल्ड नट।

स्मार्ट कॉन्ट्रैक्ट के ऑडिट करते समय, हम कभी-कभी खुद से पूछते हैं कि क्या उनका विकास उन 97% से संबंधित है, जहां अनुकूलन के बारे में सोचने की कोई आवश्यकता नहीं है या हम उन 3% मामलों से निपट रहे हैं जहां यह महत्वपूर्ण है। हमारी राय में, दूसरे की संभावना अधिक है। अन्य अनुप्रयोगों के विपरीत, स्मार्ट कॉन्ट्रैक्ट्स अपडेट नहीं किए जाते हैं, उन्हें "चलते-फिरते" ऑप्टिमाइज़ नहीं किया जा सकता (बशर्ते कि उनका एल्गोरिथ्म निर्धारित न हो, लेकिन यह एक अलग विषय है)।
प्रारंभिक अनुबंध अनुकूलन के पक्ष में दूसरा तर्क यह है कि अधिकांश प्रणालियों के विपरीत, जहां उप-इष्टतमता केवल एक पैमाने पर प्रकट होती है, हार्डवेयर और पर्यावरण की बारीकियों से संबंधित है, बड़ी संख्या में मीट्रिक द्वारा मापा जाता है, एक स्मार्ट अनुबंध में अनिवार्य रूप से एकमात्र गैस मीट्रिक - गैस की खपत होती है।
इसलिए, एक अनुबंध की प्रभावशीलता का मूल्यांकन करना तकनीकी रूप से आसान है, लेकिन डेवलपर्स अक्सर अपने अंतर्ज्ञान पर भरोसा करते हैं और उसी अंधे "समय से पहले अनुकूलन" करते हैं, जिसके बारे में प्रोफेसर नुट ने बात की थी। हम जाँचेंगे कि चर की गहराई को चुनने के उदाहरण से समाधान कितना सहज है। इस उदाहरण में, जैसा कि अधिकांश व्यावहारिक मामलों में, हम बचत हासिल नहीं करेंगे, और यहां तक कि इसके विपरीत, हमारा अनुबंध गैस खपत के मामले में अधिक महंगा हो जाएगा।
किस तरह की गैस?
Ethereum एक वैश्विक कंप्यूटर की तरह है, जिसका "प्रोसेसर" EVM वर्चुअल मशीन है, "प्रोग्राम कोड" एक स्मार्ट कॉन्ट्रैक्ट में रिकॉर्ड किए गए कमांड और डेटा का एक क्रम है, और कॉल बाहरी दुनिया से लेनदेन हैं। लेनदेन संबंधित संरचनाओं में पैक किए जाते हैं - ब्लॉक जो हर कुछ सेकंड में एक बार होते हैं। और चूंकि ब्लॉक का आकार परिभाषा सीमित है, और प्रसंस्करण प्रोटोकॉल नियतात्मक है (नेटवर्क के सभी नोड्स द्वारा ब्लॉक में सभी लेनदेन की एक समान प्रसंस्करण की आवश्यकता है), तो नोड्स के सीमित संसाधन के साथ संभावित असीमित मांग को पूरा करने और DoS के खिलाफ सुरक्षा के लिए, सिस्टम को एक उचित एल्गोरिदम प्रदान करना होगा, जिसके लिए सेवा का अनुरोध करने का अनुरोध करें। और जिसकी अनदेखी, कई सार्वजनिक ब्लॉकचेन में एक तंत्र के रूप में, एक सरल सिद्धांत है - प्रेषक अपने ट्रांस का प्रदर्शन करने के लिए खनिक को पारिश्रमिक की राशि चुन सकता है ktsii और खान में काम करनेवाला चुनता जिसका जरूरतों एक ब्लॉक शामिल हैं, और जिसका नहीं, खुद के लिए सबसे अधिक लाभदायक चुनने।
उदाहरण के लिए, बिटकॉइन में, जहां ब्लॉक एक मेगाबाइट तक सीमित है, खनिक अपनी लंबाई और प्रस्तावित कमीशन के आधार पर लेनदेन को शामिल करने का विकल्प चुनता है या नहीं।
अधिक जटिल एथेरियम प्रोटोकॉल के लिए, यह दृष्टिकोण उपयुक्त नहीं है, क्योंकि एक एकल बाइट एक ऑपरेशन की अनुपस्थिति (उदाहरण के लिए, STOP कोड) और भंडारण (SSTORE) के लिए महंगी और धीमी गति से लिखने के संचालन दोनों का प्रतिनिधित्व कर सकता है। इसलिए, हवा पर प्रत्येक ऑप-कोड के लिए इसकी स्वयं की कीमत प्रदान की जाती है, जो कि इसके संसाधन खपत पर निर्भर करता है।
प्रोटोकॉल विनिर्देश से शुल्क अनुसूची
विभिन्न प्रकार के संचालन के लिए गैस प्रवाह की तालिका।
इथेरियम येलो पेपर प्रोटोकॉल विनिर्देश से।
बिटकॉइन के विपरीत, एथेरियम लेनदेन का प्रेषक क्रिप्टोक्यूरेंसी में कमीशन निर्धारित नहीं करता है, लेकिन गैस की अधिकतम मात्रा जो वह खर्च करने के लिए तैयार है -
स्टार्टगास और गैस की प्रति यूनिट कीमत -
गैसप्राइस । जब वर्चुअल मशीन कोड को निष्पादित करती है, तो प्रत्येक बाद के ऑपरेशन के लिए गैस की मात्रा स्टार्टगैस से घटा दी जाती है जब तक या तो कोड से बाहर निकल जाता है या गैस बाहर निकल जाती है। जाहिर है, इस तरह के एक अजीब नाम का उपयोग इस इकाई के काम के लिए किया जाता है - लेन-देन कार की तरह गैस से भरा होता है, और यह गंतव्य बिंदु तक पहुंच जाएगा या नहीं, इस बात पर निर्भर करता है कि टैंक में पर्याप्त मात्रा भरी हुई है या नहीं। कोड निष्पादन के पूरा होने पर, प्रेषक (
वी प्रति गैस) द्वारा निर्धारित मूल्य से वास्तव में खपत गैस को गुणा करके प्राप्त हवा की मात्रा को लेनदेन प्रेषक से डेबिट किया जाता है। वैश्विक नेटवर्क में, यह "खनन" ब्लॉक के क्षण में होता है, जिसमें संबंधित लेनदेन शामिल होता है, और रीमिक्स वातावरण में, लेनदेन तुरंत "खनन" होता है, बिना किसी शुल्क के और बिना किसी शर्त के।
हमारा टूल - रीमिक्स आईडीई
गैस की खपत के "रूपरेखा" के लिए, हम
रीमिक्स IDE के Ethereum अनुबंध को विकसित करने के लिए ऑनलाइन वातावरण का उपयोग करेंगे। इस IDE में सिंटैक्स हाइलाइटिंग कोड एडिटर, आर्टिफिशियल व्यूअर, कॉन्ट्रैक्ट इंटरफेस रेंडर, वर्चुअल मशीन विजुअल डीबगर, जेएस कंपाइलर्स ऑफ ऑल संभव वर्जन और कई अन्य महत्वपूर्ण टूल्स हैं। मैं अत्यधिक उसके साथ ईथर का अध्ययन शुरू करने की सलाह देता हूं। एक अतिरिक्त प्लस यह है कि इसे स्थापना की आवश्यकता नहीं है - बस इसे
आधिकारिक साइट से ब्राउज़र में खोलें।
चर प्रकार चयन
सॉलिडिटी लैंग्वेज की विशिष्टता डेवलपर को पूर्णांक प्रकार के बत्तीस बिट्स प्रदान करती है - 8 से 256 बिट्स तक। कल्पना करें कि आप एक स्मार्ट अनुबंध विकसित कर रहे हैं जिसे किसी व्यक्ति की आयु को सालों में संग्रहीत करने के लिए डिज़ाइन किया गया है। आप किस बिट गहराई का चयन करते हैं?
एक विशिष्ट कार्य के लिए न्यूनतम पर्याप्त प्रकार का चयन करना काफी स्वाभाविक होगा - uint8 यहां गणितीय रूप से फिट होगा। यह मान लेना तर्कसंगत होगा कि ब्लॉकचेन पर हम जिस छोटी वस्तु को स्टोर करते हैं और कम मेमोरी जिसे हम निष्पादन पर खर्च करते हैं, हमारे पास जितना कम ओवरहेड होगा, हम उतना ही कम भुगतान करेंगे। लेकिन ज्यादातर मामलों में, यह धारणा गलत होगी।
प्रयोग के लिए, हम सबसे सरल अनुबंध लेते हैं जो आधिकारिक
सॉलिडिटी प्रलेखन प्रदान करता है और इसे दो संस्करणों में इकट्ठा करता है - चर प्रकार uint256 और 32 गुना छोटे प्रकार का उपयोग करके - uint8।
simpleStorage_uint256.sol pragma solidity ^0.4.0; contract SimpleStorage { //uint is alias for uint256 uint storedData; function set(uint x) public { storedData = x; } function get() public view returns (uint) { return storedData; } }
pragma solidity ^0.4.0; contract SimpleStorage { //uint is alias for uint256 uint storedData; function set(uint x) public { storedData = x; } function get() public view returns (uint) { return storedData; } }
simpleStorage_uint8.sol pragma solidity ^0.4.0; contract SimpleStorage { uint8 storedData; function set(uint8 x) public { storedData = x; } function get() public view returns (uint) { return storedData; } }
मापने "बचत"
इसलिए, अनुबंध बनाए जाते हैं, रीमिक्स में लोड किए जाते हैं, तैनात किए जाते हैं, और .set () तरीकों से कॉल लेनदेन द्वारा निष्पादित किए जाते हैं। हम क्या देखते हैं?
एक लंबे प्रकार की रिकॉर्डिंग एक छोटी से अधिक महंगी है - 20464 बनाम 20205 गैस इकाइयाँ! कैसे? क्यों? चलो यह पता लगाने!

स्टोर uint8 बनाम uint256
लगातार भंडारण के लिए लिखना स्पष्ट कारणों में प्रोटोकॉल में सबसे महंगा संचालन में से एक है: सबसे पहले, राज्य को रिकॉर्ड करने से डिस्क नोड का आकार पूर्ण नोड द्वारा आवश्यक बढ़ जाता है। इस भंडारण का आकार लगातार बढ़ रहा है, और अधिक राज्य नोड्स में संग्रहीत होते हैं, धीमी गति से सिंक्रनाइज़ेशन होता है, उच्चतर अवसंरचना आवश्यकता (विभाजन आकार, iops की संख्या) होती है। पीक समय में, यह धीमी डिस्क आईओ ऑपरेशन है जो पूरे नेटवर्क के प्रदर्शन को निर्धारित करता है।
यह उम्मीद करना तर्कसंगत होगा कि uint8 का भंडारण uint256 की तुलना में दस गुना सस्ता होना चाहिए। हालाँकि, डिबगर में आप देख सकते हैं कि दोनों मान 256-बिट मान के रूप में स्टोरेज स्लॉट में बिल्कुल समान हैं।

और इस विशेष मामले में, uint8 का उपयोग भंडारण में लिखने की लागत में कोई लाभ नहीं देता है।
हैंडलिंग uint8 बनाम uint256
हो सकता है कि uint8 के साथ काम करते समय हमें लाभ मिलेगा यदि भंडारण के दौरान नहीं, तो कम से कम जब स्मृति में डेटा में हेरफेर हो? निम्नलिखित विभिन्न प्रकार के चर के लिए एक ही फ़ंक्शन के निर्देशों की तुलना करता है।

आप देख सकते हैं कि uint8 के साथ संचालन uint256 की तुलना में
और भी
अधिक निर्देश हैं । ऐसा इसलिए है क्योंकि मशीन 8-बिट मान को मूल 256-बिट शब्द में परिवर्तित करता है, और परिणामस्वरूप, कोड अतिरिक्त निर्देशों से घिरा हुआ है जो प्रेषक भुगतान करता है। न केवल लेखन, बल्कि एक uint8 प्रकार के साथ कोड निष्पादित करना इस मामले में अधिक महंगा है।
छोटे प्रकारों का उपयोग कहां तक उचित हो सकता है?
हमारी टीम लंबे समय से स्मार्ट कॉन्ट्रैक्ट्स के ऑडिट में लगी हुई है, और अभी तक एक भी व्यावहारिक मामला नहीं आया है जहां ऑडिट के लिए प्रदान किए गए कोड में एक छोटे प्रकार के उपयोग से बचत होगी। इस बीच, कुछ बहुत विशिष्ट मामलों में, बचत सैद्धांतिक रूप से संभव है। उदाहरण के लिए, यदि आपका अनुबंध बड़ी संख्या में छोटे राज्य चर या संरचनाएं संग्रहीत करता है, तो उन्हें कम भंडारण स्लॉट में पैक किया जा सकता है।
निम्नलिखित उदाहरण में अंतर सबसे स्पष्ट होगा:
1. 32 चर uint256 के साथ अनुबंध
simpleStorage_32x_uint256.sol pragma solidity ^0.4.0; contract SimpleStorage { uint storedData1; uint storedData2; uint storedData3; uint storedData4; uint storedData5; uint storedData6; uint storedData7; uint storedData8; uint storedData9; uint storedData10; uint storedData11; uint storedData12; uint storedData13; uint storedData14; uint storedData15; uint storedData16; uint storedData17; uint storedData18; uint storedData19; uint storedData20; uint storedData21; uint storedData22; uint storedData23; uint storedData24; uint storedData25; uint storedData26; uint storedData27; uint storedData28; uint storedData29; uint storedData30; uint storedData31; uint storedData32; function set(uint x) public { storedData1 = x; storedData2 = x; storedData3 = x; storedData4 = x; storedData5 = x; storedData6 = x; storedData7 = x; storedData8 = x; storedData9 = x; storedData10 = x; storedData11 = x; storedData12 = x; storedData13 = x; storedData14 = x; storedData15 = x; storedData16 = x; storedData17 = x; storedData18 = x; storedData19 = x; storedData20 = x; storedData21 = x; storedData22 = x; storedData23 = x; storedData24 = x; storedData25 = x; storedData26 = x; storedData27 = x; storedData28 = x; storedData29 = x; storedData30 = x; storedData31 = x; storedData32 = x; } function get() public view returns (uint) { return storedData1; } }
2. 32 uint8 चर के साथ अनुबंध
simpleStorage_32x_uint8.sol pragma solidity ^0.4.0; contract SimpleStorage { uint8 storedData1; uint8 storedData2; uint8 storedData3; uint8 storedData4; uint8 storedData5; uint8 storedData6; uint8 storedData7; uint8 storedData8; uint8 storedData9; uint8 storedData10; uint8 storedData11; uint8 storedData12; uint8 storedData13; uint8 storedData14; uint8 storedData15; uint8 storedData16; uint8 storedData17; uint8 storedData18; uint8 storedData19; uint8 storedData20; uint8 storedData21; uint8 storedData22; uint8 storedData23; uint8 storedData24; uint8 storedData25; uint8 storedData26; uint8 storedData27; uint8 storedData28; uint8 storedData29; uint8 storedData30; uint8 storedData31; uint8 storedData32; function set(uint8 x) public { storedData1 = x; storedData2 = x; storedData3 = x; storedData4 = x; storedData5 = x; storedData6 = x; storedData7 = x; storedData8 = x; storedData9 = x; storedData10 = x; storedData11 = x; storedData12 = x; storedData13 = x; storedData14 = x; storedData15 = x; storedData16 = x; storedData17 = x; storedData18 = x; storedData19 = x; storedData20 = x; storedData21 = x; storedData22 = x; storedData23 = x; storedData24 = x; storedData25 = x; storedData26 = x; storedData27 = x; storedData28 = x; storedData29 = x; storedData30 = x; storedData31 = x; storedData32 = x; } function get() public view returns (uint) { return storedData1; } }
पहले अनुबंध की तैनाती (32 uint256) की लागत कम होगी - केवल 89941 गैस, लेकिन .set () के बाद से अधिक महंगा होगा। यह भंडारण में 256 स्लॉट्स पर कब्जा कर लेगा, जिसकी कीमत प्रत्येक कॉल के लिए 640,639 गैस होगी। दूसरा अनुबंध (32 uint8) तैनाती के समय (221663 गैस) होने पर ढाई गुना अधिक महंगा होगा, लेकिन inset () विधि के लिए प्रत्येक कॉल बहुत सस्ता होगा, क्योंकि चरण (185291 गैस) के केवल एक सेल को बदलता है।
क्या इस तरह के अनुकूलन को लागू किया जाना चाहिए?
टाइप ऑप्टिमाइज़ेशन का प्रभाव कितना महत्वपूर्ण है, यह एक महत्वपूर्ण बिंदु है। जैसा कि आप देख सकते हैं, यहां तक कि इस तरह के एक विशेष रूप से चयनित, सिंथेटिक मामले के लिए, हमें कई अंतर
नहीं मिले । Uint8 या uint256 का उपयोग करने का विकल्प इस तथ्य का एक उदाहरण है कि अनुकूलन को या तो सार्थक रूप से लागू किया जाना चाहिए (उपकरण, समझ के साथ), या इसके बारे में बिल्कुल भी नहीं सोचना चाहिए। यहाँ कुछ सामान्य दिशानिर्देश दिए गए हैं:
- यदि अनुबंध में रिपॉजिटरी में कई छोटी संख्याएं या कॉम्पैक्ट संरचनाएं हैं, तो आप अनुकूलन के बारे में सोच सकते हैं;
- यदि आप "संक्षिप्त" प्रकार का उपयोग करते हैं - याद रखें कि अधिक- / कम प्रवाह कमजोरियों के बारे में ;
- मेमोरी चर और फ़ंक्शन तर्क के लिए जो रिपॉजिटरी को नहीं लिखे गए हैं, हमेशा देशी प्रकार uint256 (या इसके उपनाम उंट) का उपयोग करना बेहतर होता है। उदाहरण के लिए, लिस्ट इटिनेटर को uint8 पर सेट करने का कोई मतलब नहीं है - बस खोना;
- संकलक के लिए भंडारण स्लॉट में सही पैकेजिंग के लिए बहुत महत्व का अनुबंध में चर का क्रम है ।
संदर्भ
मैं सलाह के साथ अंत नहीं होगा जिसमें कोई मतभेद नहीं है: विकास उपकरण के साथ प्रयोग करें, भाषा, पुस्तकालय और रूपरेखा के विनिर्देशों को जानें। यहाँ सबसे उपयोगी हैं, मेरी राय में, Ethereum मंच के बारे में सीखना शुरू करने के लिए लिंक:
- रीमिक्स अनुबंध विकास पर्यावरण एक बहुत ही कार्यात्मक ब्राउज़र-आधारित आईडीई है;
- सॉलिडिटी लैंग्वेज के स्पेसिफिकेशन , लिंक विशेष रूप से स्टेट वैरिएबल लेआउट पर सेक्शन में जाएगा;
- प्रसिद्ध OpenZeppelin टीम से एक बहुत ही दिलचस्प अनुबंध भंडार । टोकन के कार्यान्वयन के उदाहरण, भीड़ के अनुबंध और सबसे महत्वपूर्ण बात - सेफमैथ लाइब्रेरी, वह जो प्रकारों के साथ सुरक्षित रूप से काम करने में मदद करता है;
- एथेरियम यलो पेपर , एथेरियम वर्चुअल मशीन का औपचारिक विनिर्देश;
- Ethereum व्हाइट पेपर , Ethereum प्लेटफॉर्म की विशिष्टता, बड़ी संख्या में लिंक के साथ एक अधिक सामान्य और उच्च-स्तरीय दस्तावेज़;
- 25 मिनट में Ethereum , मंच के निर्माता विटालिक यूटेरिन से Ethereum के लिए एक छोटी लेकिन फिर भी कैपेसिटिव तकनीकी परिचय;
- इथरस्कैन ब्लॉकचेन एक्सप्लोरर , वास्तविक ईथर दुनिया में एक खिड़की, ब्लॉक का एक ब्राउज़र, लेनदेन, टोकन, मुख्य नेटवर्क पर अनुबंध। इथरस्कैन पर आपको परीक्षण नेटवर्क रिंकीबी, रोपस्टेन, कोवन (मुफ्त प्रसारण वाले नेटवर्क, विभिन्न आम सहमति प्रोटोकॉल पर निर्मित) के लिए एक्सप्लोरर मिलेगा।