Praėjusį mėnesį paskelbiau straipsnį, kuriame pabrėžiau, kaip kūrėjai gali žymiai sumažinti dujų sąnaudas pasirinkdami tinkamus saugyklos tipus savo „Solidity“ išmaniosiose sutartyse. Ši tema sulaukė didelio susidomėjimo, pabrėždama nuolatinį kūrėjo siekį efektyviau naudoti dujas. Kadangi Ethereum virtualios mašinos (EVM) tinklų populiarumas ir toliau auga, svarbu sumažinti operacijų mokesčius, kad Web3 programos būtų prieinamesnės ir ekonomiškesnės.
Šiame tolesniame straipsnyje aš tęsiu tyrinėjimą dujų optimizavimo metodai Solidity išmaniosiose sutartyse. Be saugyklos tipo pasirinkimo, kūrėjai gali naudoti daugybę kitų strategijų, kad padidintų savo išmaniųjų sutarčių efektyvumą. Įgyvendindami šiuos metodus, kūrėjai gali ne tik mažesni dujų mokesčiai bet ir pagerinti bendrą našumą ir vartotojo patirtį jų decentralizuotų programų (DApps). Dujų optimizavimo siekis yra labai svarbus siekiant EVM tinklų mastelio ir tvarumo, todėl tai yra pagrindinė ateities Web3 kūrimo sritis.
Dujų optimizavimo būdai
1. Sandėliavimo vietos
Kaip aptarta ankstesniame straipsnyje, tinkamo saugyklos tipo pasirinkimas yra esminis atspirties taškas optimizuojant dujų sąnaudas „blockchain“ operacijose. Ethereum virtualioji mašina (EVM) siūlo penkias saugojimo sritis: saugyklą, atmintį, skambučių duomenis, krūvą ir žurnalus. Norėdami gauti daugiau informacijos, peržiūrėkite mano ankstesnį straipsnį apie Dujų optimizavimas Solidity Smart Contracts. Čia aptariami metodai pabrėžia atminties naudojimo pranašumus, o ne saugojimą. Praktiniame pavyzdyje galima išvengti per didelio skaitymo ir rašymo saugykloje sumažinti dujų sąnaudas iki pusės!
2. Konstantos ir nekintantys kintamieji
Kaip pavyzdį paimkime šį išmanųjį kontaktą:
contract GasComparison {
uint256 public value = 250;
address public account;
constructor() {
account = msg.sender;
}
}
Šios sutarties sudarymo kaina yra 174 049 dujų. Kaip matome, mes naudojame saugyklą su egzempliorių kintamaisiais. Norėdami to išvengti, turėtume naudoti konstantas ir nekintamus kintamuosius.
Konstantos ir nekintamieji įtraukiami tiesiai į išmaniosios sutarties baitinį kodą po kompiliavimo, todėl jie nenaudoja saugyklos.
Optimizuota ankstesnės išmaniosios sutarties versija yra:
contract GasComparison {
uint256 public constant VALUE = 250;
address public immutable i_account;
constructor() {
i_account = msg.sender;
}
}
Šį kartą sumaniosios sutarties sukūrimo kaina yra 129154 dujos, 25 % mažesnė už pradinę vertę.
3. Privatūs, o ne viešieji kintamieji
Tęsdami ankstesnį pavyzdį, pastebime, kad egzempliorių kintamieji yra vieši, o tai yra problematiška dėl dviejų priežasčių. Pirma, tai pažeidžia duomenų inkapsuliavimą. Antra, jis generuoja papildomą baitinį kodą geterio funkcijai, padidindamas bendrą sutarties dydį. Didesnis sutarties dydis reiškia didesnes diegimo išlaidas, nes diegimo dujų sąnaudos yra proporcingos sutarties dydžiui.
Vienas iš optimizavimo būdų yra:
contract GasComparison {
uint256 private constant VALUE = 250;
address private immutable i_account;
constructor() {
i_account = msg.sender;
}
function getValue() public pure returns (uint256) {
return VALUE;
}
}
Padarius visus kintamuosius privačius, nesuteikiant „getter“ funkcijų, išmanioji sutartis taptų mažiau funkcionali, nes duomenys nebebus pasiekiami.
Net ir šiuo atveju sukūrimo kaina buvo sumažinta iki 92 289 dujos, 28 % mažesnė nei ankstesniu atveju ir 46 % mažesnis nei pirmuoju atveju!
PS Jei būtume laikę VERTĖ kintamasis viešas ir nepridėjo getValue funkcija, tiek pat dujų būtų sunaudota sudarant sutartį.
4. Naudokite sąsajas
„Solidity“ sąsajų naudojimas gali žymiai sumažinti bendrą išmaniosios sutarties sudaryto baito kodo dydįnes sąsajos neapima jų funkcijų įgyvendinimo. Dėl to sutarties dydis yra mažesnis, o tai savo ruožtu sumažina diegimo išlaidas, nes diegimo dujų sąnaudos yra proporcingos sutarties dydžiui.
Be to, skambinimo funkcijos per sąsajas gali būti efektyvesnės dujoms. Kadangi sąsajos apima tik funkcijų parašus, šių skambučių baitų kodą galima optimizuoti. Dėl šio optimizavimo galima sutaupyti dujų, palyginti su skambinimo funkcijomis, tiesiogiai apibrėžtomis didesnėje sutartyje, kurioje yra papildoma logika ir būsena.
Nors sąsajų naudojimas gali būti naudingas sudėtingoms išmaniosioms sutartims ir funkcijoms, tai ne visada gali būti naudinga paprastesnėms sutartims. Ankstesniuose skyriuose aptartame pavyzdyje sąsajos pridėjimas iš tikrųjų gali padidinti dujų sąnaudas sudarant paprastas sutartis.
5. Paveldėjimas prieš kompoziciją
Tęsdami sąsajos idėją, pasiekiame paveldėjimą. Peržiūrėkite šias išmaniąsias sutartis:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
contract Employee {
address public account;
constructor() {
account = msg.sender;
}
}
contract Manager {
Employee private employee;
constructor(address _employeeAddress) {
employee = Employee(_employeeAddress);
}
function getEmployeeAccount() external view returns (address) {
return employee.account();
}
}
contract Executable {
Manager public manager;
constructor(address _employeeAddress) {
manager = new Manager(_employeeAddress);
}
function getMangerAccount() external view returns (address) {
return manager.getEmployeeAccount();
}
}
Čia turime 2 išmaniąsias sutartis, kurios sąveikauja per kompoziciją. Naudojimo atvejis yra mažiau svarbus; noriu pabrėžti išorinį skambutį, kuris Vadovas reikia padaryti, kad gautumėte Darbuotojas sąskaitą. The getManagerAccount skambino iš Vykdomas sąskaita sunaudos 13 545 dujos.
Mes galime tai optimizuoti naudodami paveldėjimą:
contract Employee {
address public account;
constructor() {
account = msg.sender;
}
}
contract Manager is Employee{
}
contract Executable {
Manager public manager;
constructor(){
manager = new Manager();
}
function getMangerAccount() external view returns (address) {
return manager.account();
}
}
Šį kartą getManagerAccount užtruks tik 8 014 dujos, 40 proc. mažiau nei ankstesniu atveju!
6. Kintamųjų dydis
Baitai ir sveikieji skaičiai yra vieni dažniausiai Solidity naudojamų kintamųjų tipų. Nors „Ethereum“ virtualioji mašina (EVM) veikia su 32 baitų ilgiu, tokio ilgio kintamuosius pasirinkti kiekvienam atvejui nėra idealu, jei tikslas yra dujų optimizavimas.
Baitai
Pažvelkime į šią išmaniąją sutartį:
contract BytesComparison {
bytes32 public constant LONG_MESSAGE="Hello, world! This is a longer .";
bytes32 public constant MEDIUM_MESSAGE="Hello, world!";
bytes32 public constant SHORT_MESSAGE="H";
function concatenateBytes32() public pure returns (bytes memory) {
bytes memory concatenated = new bytes(32 * 3);
for (uint i = 0; i
Vykdymo kaina concatenate Bytes32 yra 28 909 dujos.
Kalbant apie dujas, dirbant su baitais rekomenduojama optimizuoti, kad dydis būtų sumažintas iki naudojamos vertės. Šiuo atveju optimizuota šios sutarties versija būtų:
contract BytesComparison {
bytes32 public constant LONG_MESSAGE="Hello, world! This is a longer .";
bytes16 public constant MEDIUM_MESSAGE="Hello, world!";
bytes1 public constant SHORT_MESSAGE="H";
function concatenateBytes() public pure returns (bytes memory) {
// Create a bytes array to hold the concatenated result
bytes memory concatenated = new bytes(32 + 16 + 1);
for (uint i = 0; i
Šiuo atveju concatenateBytes vykdymas yra 12 011 dujų, 59% mažesnis nei ankstesniu atveju.
Tarpt
Tačiau tai netaikoma sveikųjų skaičių tipams. Nors gali atrodyti, kad naudojant int16 būtų ekonomiškesnis nei int256taip nėra. Kalbant apie sveikuosius kintamuosius, rekomenduojama naudoti 256 bitų versijas: int256 ir uint256.
Ethereum virtuali mašina (EVM) veikia su 256 bitų žodžio dydžiu. Norint juos deklaruoti skirtingais dydžiais, Solidity reikės atlikti papildomas operacijas, kad įtrauktų juos į 256 bitų žodžio dydį, todėl sunaudos daugiau dujų.
Pažvelkime į šią paprastą išmaniąją sutartį:
contract IntComparison {
int128 public a=-55;
uint256 public b=2;
uint8 public c=1;
//Method which does the addition of the variables.
}
Sukūrimo kaina bus tokia 147 373 dujos. Jei optimizuosime, kaip minėta aukščiau, jis atrodys taip:
contract IntComparison {
int256 public a=-55;
uint256 public b=2;
uint256 public c=1;
//Method which does the addition of the variables.
}
Sukūrimo kaina šį kartą bus 131 632 dujos, 10% mažiau nei ankstesnis atvejis.
Apsvarstykite, kad pagal pirmąjį scenarijų mes kūrėme tik paprastą sutartį be jokių sudėtingų funkcijų. Tokioms funkcijoms gali prireikti pakeisti tipą, todėl gali sunaudoti daugiau dujų.
Pakavimo egzempliorių kintamieji
Yra atvejų, kai privatiems kintamiesiems rekomenduojama naudoti mažesnius tipus. Šie mažesni tipai turėtų būti naudojami, kai jie nėra susiję su logika, kuriai Solidity reikia atlikti papildomas operacijas. Be to, jie turėtų būti deklaruojami tam tikra tvarka, kad būtų optimizuotas saugojimas. Sudėję juos į vieną 32 baitų saugojimo lizdą, galime optimizuoti saugojimą ir sutaupyti šiek tiek dujų.
Jei ankstesnėje išmaniojoje sutartyje nebuvo atlikti sudėtingi skaičiavimai, rekomenduojama naudoti šią optimizuotą versiją naudojant paketą:
contract PackingComparison {
uint8 public c=1;
int128 public a=-55;
uint256 public b=2;
}
Sukūrimo kaina šį kartą bus 125 523 dujos, 15% mažiau nei ankstesnis atvejis.
7. Fiksuotas dydis, palyginti su dinaminiais kintamaisiais
Fiksuoto dydžio kintamieji sunaudoja mažiau dujų nei dinamiški Solidity pirmiausia dėl to, kaip Ethereum virtualioji mašina (EVM) tvarko duomenų saugojimą ir prieigą. Fiksuoto dydžio kintamieji turi nuspėjamą saugyklos išdėstymą. EVM tiksliai žino, kur saugomas kiekvienas fiksuoto dydžio kintamasis, todėl galima efektyviai pasiekti ir saugoti. Priešingai, dinaminiai kintamieji, pvz., eilutės, baitai ir masyvai, gali skirtis, todėl norint valdyti jų ilgį ir vietą saugykloje, reikia papildomų išlaidų. Tai apima papildomas operacijas, skirtas apskaičiuoti poslinkius ir valdyti rodykles, o tai padidina dujų suvartojimą.
Nors tai taikoma dideliems masyvams ir sudėtingoms operacijoms, paprastais atvejais negalėsime pastebėti jokio skirtumo.
Naudokite optimizavimo priemonę
Įjunkite Solidity Compiler optimizavimo režimą! Jis supaprastina sudėtingas išraiškas, sumažindamas kodo dydį ir vykdymo išlaidas, o tai sumažina sutarčių diegimui ir išoriniams skambučiams reikalingą dujų kiekį. Ji taip pat specializuojasi ir įtraukia funkcijas. Nors įtraukimas gali padidinti kodo dydį, dažnai tai leidžia dar labiau supaprastinti ir padidinti efektyvumą.
Prieš įdiegdami sutartį, suaktyvinkite optimizavimo priemonę, kai kompiliuojate naudodami:
solc –optimize –bin sourceFile.sol
Pagal numatytuosius nustatymus optimizavimo priemonė optimizuos sutartį, darant prielaidą, kad ji bus iškviesta 200 kartų per visą jos galiojimo laiką (konkrečiau, kiekvienas operacijos kodas vykdomas maždaug 200 kartų). Jei norite, kad pradinis sutarties diegimas būtų pigesnis, o vėlesnis funkcijų vykdymas – brangesnis, nustatykite –optimize-runs=1. Jei tikitės daug operacijų ir jums nerūpi didesnės diegimo išlaidos ir išvesties dydis, nustatykite –optimize-runs iki didelio skaičiaus.
Yra įvairių strategijų, kaip sumažinti dujų suvartojimą optimizuojant Solidity kodą. Svarbiausia yra pasirinkti tinkamus metodus kiekvienam konkrečiam atvejui, kurį reikia optimizuoti. Teisingai pasirinkus, dujų sąnaudas dažnai galima sumažinti iki 50 %. Taikydami šiuos optimizavimus, kūrėjai gali pagerinti savo decentralizuotų programų (DApps) efektyvumą, našumą ir vartotojo patirtį, taip prisidedant prie Ethereum virtualiosios mašinos (EVM) tinklų mastelio ir tvarumo.
Kadangi mes ir toliau tobuliname šią praktiką, Web3 kūrimo ateitis atrodo vis perspektyvesnė.
Tvirtumo dokumentacija
Cyfrin tinklaraštis: Solidity Gas optimizavimo patarimai