Как работает базовый газ
В транзакциях Aptos по умолчанию взимается базовая плата за газ, независимо от рыночных условий. Для каждой транзакции эта сумма "базового газа" основывается на трех условиях:
Инструкции.
Хранение.
Полезная нагрузка.
Чем больше вызовов функций, условных операторов ветвления и т.д. требуется для выполнения транзакции, тем больше газа на инструкции. Аналогично, чем больше чтений из глобального хранилища и записей в него требует транзакция, тем больше газа для хранения она будет стоить. Наконец, чем больше байт в полезной нагрузке транзакции, тем больше она будет стоить.
Как объясняется в разделе "Принципы оптимизации", газ из хранилищ оказывает самое большое влияние на базовый газ.
Инструктивный газ
Основные параметры газа команд определяются в файле instr.rs
и включают следующие типы команд:
Без операции
Параметр | Значение |
---|---|
| Отсутствие операции |
Управляющий поток
Параметр | Значение |
---|---|
| Возврат |
| Прервать |
| Выполнение условной истинной ветки |
| Выполнение условной ложной ветки |
| Ветка |
Стек
Параметр | Значение |
---|---|
| Вытащить из стека |
| Загрузить |
| Загрузить |
| Загрузить |
| Загрузить |
| Загрузить |
| Базовые затраты на постоянную нагрузку |
| Побайтовая стоимость загрузки константы |
Локальный масштаб
Параметр | Значение |
---|---|
| Неотъемлемый заем |
| Взаимно занимать |
| Безвозвратно занять поле |
| Взаимное заимствование поля |
| |
| |
| Базовая стоимость копирования |
| |
| Move |
|
Вызов
Параметр | Значение |
---|---|
| Базовая стоимость вызова функции |
| Стоимость одного аргумента функции |
| |
| Стоимость за аргумент типа |
|
Структуры
Параметр | Значение |
---|---|
| Базовая стоимость пакета |
| Стоимость пакета |
| |
| |
| Базовая стоимость распаковки |
| Стоимость распаковки |
| |
|
Рекомендации
Параметр | Значение |
---|---|
| Базовая стоимость чтения из ссылки |
| |
| Базовая стоимость записи в базу данных |
| Заморозить ссылку |
Кастинг
Параметр | Значение |
---|---|
| Каст на |
| Каст на |
| Каст на |
Арифметика
Параметр | Значение |
---|---|
| Добавить |
| Вычесть |
| Умножить |
| Модуль |
| Разделить |
Побитовая
Параметр | Значение |
---|---|
|
|
|
|
|
|
| Сдвиг влево: |
| Сдвиг вправо: |
Логика
Параметр | Значение |
---|---|
|
|
|
|
|
|
Сравнение
Параметр | Значение |
---|---|
| Меньше, чем: |
| Больше, чем: |
| Меньше или равно: |
| Больше или равно: |
| Базовая стоимость равенства: |
| |
| Базовая стоимость не равна: |
|
Глобальное хранилище
Параметр | Значение |
---|---|
| Базовая стоимость неизменного займа: |
| |
| Базовая стоимость взаимного заимствования: |
| |
| Базовая стоимость проверки существования: |
| |
| Базовая стоимость для move: |
| |
| Базовая стоимость move: |
|
Векторы
Параметр | Значение |
---|---|
| Длина вектора |
| Неизменно заимствовать элемент |
| Взаимное заимствование элемента |
| Отталкивание |
| Сзади |
| Поменяйте элементы местами |
| Базовая стоимость упаковки вектора |
| Стоимость пакета вектора на один элемент |
| Базовая стоимость распаковки вектора |
| Базовая стоимость распаковки вектора на элемент |
Дополнительные параметры газа для хранилища определены в table.rs
, move_stdlib.rs
и других различных исходных файлах в aptos-gas/src/
.
Газ для хранилища
Определение газа для хранилищ содержится в файле storage_gas.move
, который сопровождается полным и внутренне связанным файлом DocGen storage_gas.md
.
Вкратце:
В
initialize()
,base_8192_exponential_curve()
Параметры перенастраиваются каждую эпоху через функцию
on_reconfig()
, основываясь на коэффициентах использования по элементам и байтам.Измененные параметры сохраняются в
StorageGas
, который содержит следующие поля:
Поле | Значение |
---|---|
| Стоимость чтения элемента из глобального хранилища |
| Стоимость создания элемента в глобальном хранилище |
| Стоимость перезаписи элемента в глобальном хранилище |
| Стоимость чтения байта из глобального хранилища |
| Стоимость создания байта в глобальном хранилище |
| Стоимость перезаписи байта в глобальном хранилище |
Здесь элемент - это либо ресурс, имеющий атрибут key
, либо запись в таблице, и, что примечательно, стоимость за байт оценивается по всему размеру элемента. Как указано в файле storage_gas.md
, например, если операция изменяет поле u8
в ресурсе, который имеет пять других полей u128
, стоимость записи за байт будет составлять (5 * 128) / 8 + 1 = 81(5∗128)/8+1=81 байт.
Векторы
Аналогичным образом байтовые сборы начисляются на векторы, которые потребляют байт, где:
- количество элементов в векторе
- размер элемента
это "базовый размер", который является функцией от
Более подробную информацию о размере базы вектора (технически ULEB128
) см. в спецификации последовательности BCS, которая на практике обычно занимает всего один байт, так что вектор из 100 элементов u8
занимает 100 + 1 = 101100+1=101 байт. Следовательно, согласно описанной выше методологии поэлементного чтения, чтение последнего элемента такого вектора рассматривается как чтение 101 байта.
Газ полезной нагрузки
Газ полезной нагрузки определяется в transaction.rs
, который включает газ для хранилищ с несколькими параметрами, связанными с полезной нагрузкой и ценообразованием:
Параметр | Значение |
---|---|
| Минимальное количество единиц внутреннего газа для транзакции, взимаемое в начале исполнения |
| Размер, в байтах, при превышении которого за транзакции будет взиматься дополнительная сумма за байт |
| Внутренние блоки газа, взимаемые за байт для полезной нагрузки свыше |
| Верхний предел внешних единиц газа для транзакции |
| Минимальная цена газа, допустимая для транзакции |
| Максимальная цена газа, допустимая для транзакции |
| Максимальный размер полезной нагрузки транзакции в байтах |
| Коэффициент пересчета между единицами внутреннего газа и единицами внешнего газа |
Здесь "единицы внутреннего газа" определяются как константы в исходных файлах, таких как instr.rs
и storage_gas.move
, которые являются более детализированными, чем "единицы внешнего газа", на коэффициент gas_unit_scaling_facto
r: чтобы преобразовать единицы внутреннего газа в единицы внешнего газа, разделите на gas_unit_scaling_factor
. Затем, чтобы перевести единицы внешнего газа в октаты, умножьте на "цену газа", которая обозначает количество октат за единицу внешнего газа.
Основы оптимизации
Единицы измерения и ценовые константы
На момент написания этой статьи min_price_per_gas_unit
в transaction.rs
определяется как aptos_global_constants
::GAS_UNIT_PRICE
(которая сама определяется как 100), а другие заслуживающие внимания константы transaction.rs
следующие:
Постоянная | Значение |
---|---|
| 100 |
| 10,000 |
| 10,000 |
Значение этих констант см. в разделе Газ полезной нагрузки.
Газ для хранилища
На момент написания этой статьи функция initialize()
устанавливает следующие минимальные объемы газа для хранилища:
Стиль данных | Операция | Символ | Минимальное количество внутреннего газа |
---|---|---|---|
За единицу | Читать | 300,000 | |
За единицу | Создавать | 5,000,000 | |
За единицу | Писать | 300,000 | |
За байт | Читать | 300 | |
За байт | Создавать | 5,000 | |
За байт | Писать | 5,000 |
Максимальные суммы в 100 раз больше минимальных, что означает, что при коэффициенте использования 40% или меньше, общие затраты на газ будут примерно в 1-1,5 раза больше минимальных сумм (смотрите base_8192_exponential_curve()
для вспомогательных расчетов). Следовательно, в пересчете на окты, начальные затраты на газ в сети можно оценить следующим образом (разделить внутренний газ на масштабный коэффициент, затем умножить на минимальную цену газа):
Операция | Операция | Минимальные октавы |
---|---|---|
Считывание по каждому пункту | 3000 | |
Создание по каждому пункту | 50,000 | |
Запись по каждому пункту | 3000 | |
Побайтовое считывание | 3 | |
Побайтовое создание | 50 | |
Побайтовая запись | 50 |
Здесь самой дорогой операцией на элемент является создание нового элемента (через move_to()
или добавление в таблицу), что стоит почти в 17 раз больше, чем чтение или перезапись старого элемента: .
Дополнительно:
Запись стоит столько же, сколько и чтение, в расчете на каждый элемент:
Однако в расчете на байт запись стоит столько же, сколько и создание:
Запись и создание каждого байта обходятся почти в 17 раз дороже, чем чтение каждого байта:
Поэлементное чтение стоит в 1000 раз больше, чем побайтовое:
Создание каждого элемента стоит в 1000 раз больше, чем создание каждого байта:
Запись по элементам стоит в 60 раз дороже, чем запись по байтам:
Таким образом, операции на элемент стоят в 1000 раз больше, чем операции на байт, как для чтения, так и для создания, но только в 60 раз больше для записи.
Таким образом, в отсутствие законного экономического стимула к деаллокации из глобального хранилища (через move_from()
или путем удаления из таблицы), наиболее эффективная стратегия оптимизации газа в хранилище выглядит следующим образом:
Сведите к минимуму создание каждого элемента
Отслеживайте неиспользуемые элементы и по возможности перезаписывайте их, а не создавайте новые.
Ограничьте запись каждого элемента как можно меньшим количеством элементов
Считывайте, а не записывайте, когда это возможно
Сведите к минимуму количество байтов во всех операциях, особенно при записи.
Инструкции газа
На момент написания этой статьи все операции с газом умножаются на коэффициент EXECUTION_GAS_MULTIPLIER
, определенный в файле gas_meter.rs
,, который установлен на 20. Следовательно, следующие репрезентативные операции предполагают следующие затраты на газ (разделите внутренний газ на масштабный коэффициент, затем умножьте на минимальную цену газа):
Операция | Минимальные октавы |
---|---|
Блок добавления/заимствования/удаления таблицы | 240 |
Вызов функции | 200 |
Постоянная нагрузка | 130 |
Глобальные займы | 100 |
Запрос на чтение/запись | 40 |
Загрузка | 16 |
Работа блока таблицы на один байт | 2 |
(Обратите внимание, что газ на байты в блоке таблицы не учитывает газ для хранения, который оценивается отдельно).
Для сравнения, чтение 100-байтового элемента стоит окта в минимум, что примерно в 16,5 раз больше, чем вызов функции, и в целом затраты на газ инструкций в значительной степени доминируют над затратами на газ хранилища.
Примечательно, однако, что технически все еще существует стимул для уменьшения количества вызовов функций в программе, но усилия инженеров более эффективно направлены на написание модульного, разложенного кода, направленного на снижение затрат на газ для хранения данных, а не на попытки написать повторяющиеся блоки кода с меньшим количеством вложенных функций (почти во всех случаях).
В экстремальных случаях возможно, что газ инструкций значительно превосходит газ хранилища, например, если математической функции, состоящей из циклов, требуется 10 000 итераций для сходимости; но это опять же крайний случай, и для большинства приложений газ хранилища оказывает большее влияние на базовый газ, чем газ инструкций.
Газ полезной нагрузки
На момент написания этой статьи transaction.rs
определяет минимальное количество внутреннего газа на транзакцию в 1 500 000 внутренних единиц (минимум 15 000 окта), которое увеличивается на 2 000 внутренних единиц газа (минимум 20 окта) на байт для полезной нагрузки размером более 600 байт, а максимальное количество байт в транзакции установлено на уровне 65536. Таким образом, на практике газ полезной нагрузки вряд ли будет представлять проблему.
Last updated