Хранение 256-битных данных в Ethereum потребляет около 20 тысяч газа. При этом преобразовании только 1 ГБ ресурсов хранения стоит 32 000 ETH, что составляет примерно более 100 миллионов долларов США. Не говоря уже о том, что плата за газ аристократической сети, вероятно, будет продолжать расти. В первые годы ее потребление газа было немалым. Таким образом, оптимизация Ethereum Gas является сложной проблемой при разработке Dapp, а также важным навыком для разработчиков Solidity.
Просмотрите общую сумму газа и цену, потраченную на транзакцию, просмотрите ее непосредственно в деталях транзакции.
Просмотрите трассировку транзакции и нажмите «Отслеживание четности» в сведениях о транзакции. Вы можете увидеть газ каждой внутренней транзакции, в основном вызова, вызова делегата и т. д.
Выберите geth Trace, чтобы увидеть потребление газа на уровне кода операции.
в ремиксеосновнойсуществовать Результаты выполнения транзакцииconsoleсередина Проверять。
Transaction Cost На основе стоимости отправки данных в блокчейн. Все трансакционные издержки 4 Состав предмета:
Execution Cost Стоимость основана на вычислительных операциях, выполненных на EVM в результате транзакции.
Только с помощью встроенной цепочки отладки Remix можно вывести дифференцированную стоимость транзакции. Если вы подключите другие цепочки, вы сможете получить только общую стоимость выполнения газа.
Hardhat не будет напрямую передавать статус газа. При выполнении контракта развертывания и взаимодействия с контрактом вы обычно можете получить квитанцию через хеш транзакции в обещании и получить результат из квитанции.
Сделки взаимодействия по контракту:
let res = await contract.mint(user.address, 10000);
let receipt = await hre.ethers.provider.getTransactionReceipt(res.hash);
console.log("gas used: ", receipt.gasUsed);
console.log("gas*price: ", receipt.gasUsed.mul(receipt.effectiveGasPrice));
Развертывание транзакции контракта:
let res = await contract.deployed();
let receipt = await hre.ethers.provider.getTransactionReceipt(
res.deployTransaction.hash
);
console.log("gas used: ", receipt.gasUsed);
console.log("gas*price: ", receipt.gasUsed.mul(receipt.effectiveGasPrice));
let ContractFactory = await hre.ethers.getContractFactory("ActivityToken");
let Contract = await ContractFactory.deploy("ActivityToken", "AT");
// Оценивать Развернуть газ, не очень точно, лучшего способа пока нет
console.log(
"Deploy Estimated gas:",
await ethers.provider.estimateGas(contractFactory.bytecode)
);
let res = await contract.deployed();
// Расчетный колл-контракт изгаз
console.log(
"Mint estimated Gas: ",
await contract.estimateGas.mint(deployer.address, 100000)
);
существоватькаскасерединаосновнойдаиспользоватьhardhat-gas-reporterплагин,Может существовать при запуске модульных тестов,Одновременно формируется отчет об исполнении газа. В отчете середина вы также можете увидеть средний расход газа в процессе тестирования середина для каждой отдельной функции.,Процесс развертывания потребляет газ.
Hardhat Gas Reporter — это плагин Hardhat, который можно использовать для отображения использования газа каждой функцией контракта в консоли, а также использования газа для всего контракта. Вот шаги по использованию Hardhat Gas Reporter:
npm install --save-dev hardhat-gas-reporter
require("hardhat-gas-reporter");
module.exports = {
gasReporter: {
currency: 'CHF',
gasPrice: 21
}
}
Эффект следующий:
gasReporter: {
//doc:https://github.com/cgewecke/eth-gas-reporter
enabled: true,
currency: "USD", //Евро по умолчанию, Дополнительно доллары США, CNY, HKD
// Токеном по умолчанию является ETH, Если вы перейдете на другой, вы найдете цену на coinmarketcap в режиме реального времени.
// token: "MATIC",
coinmarketcap: "59a52916-XXXX-XXXX-XXXX-2d6f56917aee", //https://coinmarketcap.com/
// По умолчанию из eth gas station api середина, чтобы получить цену eth. Для других токенов вы можете ввести gasPrice самостоятельно (рекомендуется). Или заполните gasPriceAPI (обратите внимание на ограничения вызова)
// gasPrice: 30,
// gasPriceApi:"https://api.etherscan.io/api?module=proxy&action=eth_gasPrice",
},
https://www.npmjs.com/package/hardhat-gas-reporter
Базовая формула расчета ГАЗ:
gas = txGas + dataGas + opGas
Если транзакция не создает новый контракт, txGas 21000, иначе txGas 53000. Каждый нулевой байт данных в транзакции стоит 4 индивидуальный gas,Каждыйиндивидуальный Нетнулевой байт стоит 16 индивидуальный газ. opGas означает работу всех op необходимый gas。
Вообще говоря, у opGas больше возможностей для оптимизации.
Контрактное потребление газа:
При оценке газа часто приходится идти на компромисс между двумя вышеперечисленными вариантами.
Газ, используемый различными операциями
Operation Gas Description
ADD/SUB 3 Arithmetic operation
MUL/DIV 5 Arithmetic operation
ADDMOD/MULMOD 8 Arithmetic operation
AND/OR/XOR 3 Bitwise logic operation
LT/GT/SLT/SGT/EQ 3 Comparison operation
POP 2 Stack operation
PUSH/DUP/SWAP 3 Stack operation
MLOAD/MSTORE 3 Memory operation
CALLDATALOAD 3 Calldata operation
JUMP 8 Unconditional jump
JUMPI 10 Conditional jump
SLOAD 100/2100 Storage operation (Горячий доступ/холодный доступ)
SSTORE 5,000/20,000 Storage operation
BALANCE 400 Get balance of an account
CREATE 32,000 Create a new account using CREATE
CALL 25,000 Create a new account using CALL
KECCAK256 gas_cost = 30 + 6 * data_size_words + mem_expansion_cost
LOG gas_cost = 375 + 375 * num_topics + 8 * data_size + mem_expansion_cost
Посмотреть подробности:https://ethereum.org/en/developers/docs/evm/opcodes/
На данный момент у Solidity есть два оптимизатора:
ссылка:
https://docs.soliditylang.org/zh/v0.8.19/internals/optimizer.html Документация оптимизатора
https://docs.soliditylang.org/zh/v0.8.19/yul.html# Документация по языку Yul
https://learnblockchain.cn/article/6064 Юл Руководство по началу работы
При использовании команды solc:
В настоящее время --optimize запускает оптимизатор на основе опкода для оптимизации байт-кода, а также запускает оптимизатор Yul для оптимизации внутренне сгенерированного кода Yul.
Используйте solc --ir-optimized --optimize для создания оптимизированного Yul IR, соответствующего исходному коду Solidity.
Используйте solc --strict-assembly --optimize, чтобы включить специализированную оптимизацию режима Yul.
При использовании каски:
плагин Поддерживает только Конфигурацияoptimizerодининдивидуальныйвыбирать , то есть вы не можете выбрать такие параметры, как ИК-оптимизация, и в настоящее время это режим оптимизации по умолчанию.
module.exports = {
solidity: {
version: "0.8.9",
settings: {
optimizer: {
enabled: false,
runs: 200,
},
},
},
};
Количество запусков ( --optimize-runs ) грубо оговаривается, что в течение срока действия договора (под которым можно понимать 1 год), Развернутый код каждого отдельного кода операции выполняется с определенной частотой. Это означает, что это компромиссный параметр между стоимостью выполнения кода (стоимостью развертывания) и стоимостью выполнения кода (стоимостью после развертывания). одининдивидуальный "бегать" Параметры “1” Будет создавать короткие контракты, но дорогой код выполнения. Напротив,одининдивидуальныйсравниватьбольшойиз "бегать" Параметры приведут к увеличению размера контрактов, но к более эффективному использованию кода выполнения. Максимальное значение этого параметра составляет 2^32-1。
Обратите внимание, что количество запусков не означает, что чем больше, тем лучше, и это не означает, сколько выполняется итераций оптимизации.
Простое понимание состоит в том, что прогоны — это эвристический параметр, определяющий, следует ли встраивать их. Чем больше прогонов, тем больше вероятность встраивания.
uint256 public v1;
uint256 public constant v2 = 1000;
function calculate() returns (uint256 result) {
return v1 * v2 * 10000
}
В настоящее время v2 и 10000 являются членами байт-кода, а v1 является членом переменной состояния.
Каждое чтение v1 требует дополнительной загрузки, которая будет стоить 200 газа.
Для внешнего:
публичная модификация равна внешней+внутренней , нравиться Если только указаноextenral,Эта отдельная функция из параметра не нуждается в хранении в памяти существования середина.,Вместо этого читайте непосредственно из calldataсередина., Напротив, если параметры публичной функции хранятся в памяти.
поэтому,Не используйте public, когда можно использовать external.
Этот метод оптимизации особенно эффективен для функции сравнения параметров.
Для просмотра в чистом виде:
Представление не изменит никакого состояния в блокчейне, но следует отметить, что только функция внешнего просмотра или функция публичного просмотра бесплатны, когда она вызывается извне. При вызове в транзакции по-прежнему требуются обычные вычеты. С чистым ситуация аналогичная.
Сопоставление обходится дешевле, если вам не нужно упаковывать данные или выполнять итерацию. Используя карту, также можно выполнять итерацию, используя числа в качестве ключевых индексов.
Если логика определяет длину массива, вы также можете использовать массив фиксированной длины.
Если содержимое параметра не изменяется и данные только читаются, приоритет отдается calldata. Потребление данных вызова в основном такое же, как и потребление памяти. Однако все входные параметры уже занимают хранилище в виде данных вызова. Если оно указано как память, данные данных вызова будут скопированы, и их индекс будет сохранен в памяти, которая будет сохранена. дополнительные ресурсы в этой части стоят дороже.
// calldata
function func2 (uint[] calldata nums) external {
for (uint i = 0; i < nums.length; ++i) {
...
}
}
// Memory
function func1 (uint[] memory nums) external {
for (uint i = 0; i < nums.length; ++i) {
...
}
}
Кроме того, для переменных состояния можно свести к минимуму их повторное использование в циклах.
//После оптимизации
uint sum = 0;
function p3 ( uint x ){
for ( uint i = 0 ; i < x ; i++)
sum += i; }
//После оптимизации
uint sum = 0;
function p3 ( uint x ){
uint temp = 0;
for ( uint i = 0 ; i < x ; i++)
temp += i; }
sum += temp;
//Using delete keyword
delete myVariable;
//Or assigning the value 0 if integer
myInt = 0;
//Следующие действия очистят исходное значение переменной состояния середина;
myAddresses = new address[](0);
что такое газ refund 1. Звонок по договору selfdestruct Уничтожить или отменить контракт sstore Вы можете получить его, изменив значение переменной состояния с ненулевого на пустое. gas возвращаться. 2. gas Возврат не означает, что баланс Ethereum на счету увеличится, это просто означает, что стоимость транзакции gas Сумма уменьшится. газ Лимит возврата индивидуален, что не является могу превышает текущую стоимость транзакции gas из 50%, если оно превышает, нажмите 50% Рассчитайте. 3. gas Возврат не означает, что баланс на счету инициатора транзакции может быть уменьшен, и проверка баланса по-прежнему основана на gas price * gas limit。
Отдельные индивидуальные переменные должны иметь значение unit256, насколько это возможно,поскольку EVM обрабатывает 32 байта за одну операцию, для uint8иbooliz также необходимо заполнить данные,Необходимо больше газа. (Использование uint256 уменьшит проблемы с преобразованием типов и совместимостью)
толькосуществовать При постоянном хранении,Особенно если поставить структуру середина,Структура действительнаиспользовать Маленький,В это время нужно обратить внимание на выравнивание и порядок памяти.
contract Leggo {
uint128 a;
uint128 c;
uint256 b;
}
Понимание: что такое переменная очистка
Очистка переменных (Очистка Up Variables): когдаодининдивидуальныйценитьиз Количество занятых цифр Маленький В32индивидуальныйбайт,Чтосерединабесполезныйизбит будет очищен。无论да加载到Памятьсерединаили естьсуществоватьхранилищесередина,городэтотсделай это, В противном случае это повлияет на расчет.hashили сгенерироватьcalldataи тому подобноеизлогика。
https://docs.soliditylang.org/zh/v0.8.16/internals/variable_cleanup.html
По этой же причине byte[] эффективность также низкая из-за очистки переменных. Попробуйте вместо этого использоватьbytes32 или другие байтыX.
Та же причина в том, что строка нравиться, если ее длина меньше 32, Также рассмотрите возможность использования вместо этого byteX.
// 599 gas
function useString() public returns(string memory a) {
a = "hello world!";
}
// 196 gas
function useByte() public returns(bytes32 a) {
a = bytes32("hello world!");
}
Замените require(isOwner(msg.sender), "Unauthorized") на if (!isOwner(msg.sender)){revert Unauthorized();}, чтобы уменьшить количество информации о возврате.
Поскольку сигнатура функции другая,При вызове функции,EVM должен помочь вам найти функции,Итак, согласно сигнатуре функции из числового значения Большой Маленький,Если вы найдете первым, это будет стоить меньше газа.,После нахождения из, это будет стоить больше газа.,Поэтому для особо часто вызываемых функций,нас Может Рассмотрите корректировки Чтоприоритет。 Также обратите внимание,что общедоступные переменные состояния также участвуют в поисковых вычислениях (т. е. uint256 public a иметьодининдивидуальный a() функция).
в заключение:
ссылка:
существовать Сколькоиндивидуальныйinputпараметр, Вклад организации можно увидеть данные представляют собой группу из 32 байтов.
Function: trade(address tokenGet, uint256 amountGet, address tokenGive, uint256 amountGive, uint256 expires, uint256 nonce, address user, uint8 v, bytes32 r, bytes32 s, uint256 amount) ***
MethodID: 0x0a19b14a
[0]:0000000000000000000000000000000000000000000000000000000000000000
[1]:000000000000000000000000000000000000000000000000006a94d74f430000
[2]:000000000000000000000000a92f038e486768447291ec7277fff094421cbe1c
[3]:0000000000000000000000000000000000000000000000000000000005f5e100
[4]:000000000000000000000000000000000000000000000000000000000024cd39
[5]:00000000000000000000000000000000000000000000000000000000e053cefa
[6]:000000000000000000000000a11654ff00ed063c77ae35be6c1a95b91ad9586e
[7]:000000000000000000000000000000000000000000000000000000000000001c
[8]:caa3a70dd8ab2ea89736d7c12c6a8508f59b68590016ed99b40af0bcc2de8dee
[9]:26e2347abfba108444811ae5e6ead79c7bd0434cf680aa3102596f1ab855c571
[10]:000000000000000000000000000000000000000000000000000221b262dd8000
На данный момент мы можем использовать внутреннее пространство для размещения нескольких человек, таких как uint8, addressэтот Он не займет все местоиз Комбинация параметровсуществоватьодинрост,становитьсяодининдивидуальныйuint256, Внутри контракта данные анализируются через mload.
Для входных данныхсередина каждый дополнительный индивидуальный байт увеличит газ на 68 (если все байты равны 0, он увеличит газ на 4), Для частых звонков, Сжать необходимы вводные данные из.
При оценке в первую очередь принимается решение о низкой стоимости (режим короткого замыкания, короткое замыкание rules): нравиться f(x) || g(y) должно облегчить судить оtrueиз Условное освобождениесуществоватьпередний。
Уменьшите ненужные зависимости.
избегатьсуществоватьциклсередина Делайте большое потреблениеиздействие,Merge может объединять циклы,Извлечь инвариант цикла из выражения наружу,Цикл середина позволяет избежать прямого накопления переменных состояния.,избегатьсуществоватьциклсередина Звонил несколько разarr.length。
++i лучше, чем i++, чем i+=1.
Для определения расчета вы можете использовать блок снятия отметки: require(a <= b); unchecked { x = b - a }
В основном для повторного использования библиотеки, Вообще говоря, внутренние обращения к файлам библиотеки будут стоить дешевле, но встраивание и встраивание библиотеки приведет к увеличению затрат на развертывание; А вызов внешнего метода файла библиотеки увеличит стоимость вызова.,Но размер файла развертывания больше.,поэтомудаодининдивидуальныйскладыватьсередина。
особое внимание: нравиться Если у библиотеки libсередина есть внешние или общедоступные методы, библиотеку необходимо развернуть независимо, и remix автоматически преобразует ее.И каскасередина должна обратить внимание на заполнение информации по ссылке,В противном случае будет сообщено об ошибке.
Стоимость транзакции на картинке выше: 115901.
Стоимость звонка в Бариз: 49858, 26973.
Две картинки вышеиндивидуальныйторговлятратить отдельно: 122886,146252, Последующее развертывание Бара будет дорогостоящим 146252。
Вызов метода бара изgas: 53650, 30765.
Развернуть библиотеку
let libFactory = await hre.ethers.getContractFactory("IdentityLib");
let lib = await libFactory.deploy();
await lib.deployed();
console.log("library deployed to address: ", lib.address);
Разверните контракт и свяжите библиотеку при развертывании.
let contractFactory = await hre.ethers.getContractFactory(
"CarbonIslandMethodology",
{
signer: deployer,
libraries: {
IdentityLib: lib.address,
},
}
);
let contract = await contractFactory.deploy();
await contract.deployed();
console.log("contract deployed to address: ", contract.address);
Самый маленький агент предоставляет индивидуальный код контракта с самым простым агентом.
использоватьбольшинство Маленькийактерское мастерствоиз На что следует обратить внимание : Самый Маленький агент из реализует адрес контракта не Могу измениться, а это значит, что вы не будете могу обновить свой код.
https://eips.ethereum.org/EIPS/eip-1167
https://mirror.xyz/xyyme.eth/mmUAYWFLfcHGCEFg8903SweY3Sl-xIACZNDXOJ3twz8
logизgasиспользоватьпараметрнравиться Вниз
LogDataGas uint64 = 8 // Per byte in a LOG* operation's data.
LogGas uint64 = 375 // Per LOG* operation.
LogTopicGas uint64 = 375
MemoryGas uint64 = 3
Его основная формула расчета:
gas_cost = 375 + 375 * num_topics + 8 * data_size + mem_expansion_cost
Пример индивидуального: нравиться - это 2индивидуальная тема + 200 байтизлога, стоимость изгаза:
375 (static cost)
200 = 200 bytes of memory for log.Data x 3 cost of memory = 600 gas for memory gas
2 x 375 = 750 for topic gas
8 x 200 = 1600 for log.Data cost
Total cost: 375 + 600 + 750 + 1600 = 3,325 gas units
Виден по сравнению схранилищеодининдивидуальныйuint256, Нужно 20000+ gas , чтобы сэкономить много.
краткое содержание:
Короче говоря, Меркель доказала, что может использовать один отдельный блок данных, чтобы доказать достоверность большого количества данных.
лицо без гражданствадоговорвоспользовалсяторговляданныеи Вызовы событий и другое содержимое полностью сохраняютсясуществовать Блокчейнначальствоизфакт。поэтому,Вам не нужно постоянно менять состояние контракта,而Только需发送один Ручкаторговляи пройти, хочешьхранилищеизценить Вот и все。Зависит от В SSTORE Операции обычно составляют часть транзакционных издержек, поэтому контракты без сохранения состояния потребляют только часть контрактов с сохранением состояния. Gas。
contract DataStore {
function save(bytes32 key, string value) {}
}
Затем вы можете использовать Ethereum-input-data-decoder` Чтобы напрямую проанализировать транзакцию из входных данных
npm install ethereum-input-data-decoder
### Используйте источники данных вне сети
нравитьсяipfsждать,Но сохранитесуществоватьбольшойколичество и Нет结构化данные时,Целесообразно использовать метод источника данных вне цепочки. Мы можем передавать данные в IPFS сеть,Тогда соответствующееиз Хэшценитьдержатьсуществоватьдоговорсередина,чтобы позже обратиться к этой информации.
Сокращение методов хранения в цепочке. Краткое содержание:
Index Logs | Stateless Contract | Merkle Proof | Off-chain(IPFS) | |
---|---|---|---|---|
Размер данных | Маленький | Маленький | середина Маленький | большой |
Экономия газа | Маленький (само полено тоже требует бензина) | середина | большой | большой |
Могут ли данные использоваться по договору? | не могу | не могу | Может. (поставляется с путем Меркла) | Это довольно хлопотно и требует наличия оракула или других методов для подачи данных для использования. |
нравиться Как изменить данные | Off-chain определяет, что новые данные перезаписывают старые данные. | Off-chain определяет, что новые данные перезаписывают старые данные. | Изменить оффчейн и обновить корневой ончейн | Сложнее, вам нужно реализовать метод модификации самостоятельно. |
Применимые сценарии | Нет необходимости заключать контракты серединаиспользовать переменные,Самым важным поведением в цепочке является запись,Остальные действия выполняются вне сеть.. Существует множество вариантов поведения, которые необходимо классифицировать и напомнить об их обработке вне сети. | Нет необходимости заключать контракты серединаиспользовать переменные,Самым важным поведением в цепочке является запись, Остальные действия выполняются вне сети. | Нет необходимости часто получать доступ, изменять или добавлять переменные в цепочке, в основном это записи пакетных данных и периодические отдельные запросы проверки. | Большой объем данных вне сети,и сохранитьсуществоватьнравитьсякартинаждать Нет结构化данные。основнойиспользовать ВВспомогательныйдоговорхранилищеодин Некоторые несуществоватьдоговорсерединаписатьивычислитьиздополнительные данные |
https://mirror.xyz/quentangle.eth/GxmosHtVYZaIkJjM9slpkKtZWfk8fU78FQ8oxoDNuFE
оптимизация газа
https://ethereum.stackexchange.com/questions/28813/how-to-write-an-optimized-gas-cost-smart-contract
https://medium.com/layerx/how-to-reduce-gas-cost-in-solidity-f2e5321e0395