скачать
Закрыть меню -

Стрессовое тестирование глобального состояния узла RIF Consensus Node

Published on: 22 июня, 2020

Автор: Пабло Педемонте

Введение

В 2016 году платформа RSK предложила Unitrie в качестве альтернативы реализации дерева Меркла для моделирования глобального состояния Ethereum (см. «Желтую книгу Ethereum», приложение D). По состоянию на январь 2020 года мы добавили Unitrie в качестве дополнительной функции в узел RSK Rif Consensus Node (на базе кода Besu). С ее помощью пользователи могут выбирать между двумя реализациями глобального состояния:

  1. Глобальное состояние Besu (или классическое глобальное состояние), описанное в «Желтой книге».
  2. Глобальное состояние Unitrie с поддержкой Unitrie.

Имея возможность выбирать узел RIF Consensus Node из двух разных вариантов реализации глобального состояния, мы можем сравнить достоинства каждого варианта. В этой статье мы сравним оба варианта реализации, проанализировав время построения/поиска и возможности выделения счетов до заполнения доступного хранилища данных.

Эксперимент 1: время построения и поиска

Для анализа времени построения и поиска мы провели тестирование, которое состоит из добавления заданного количества счетов в изначально пустое глобальное состояние Ethereum и фиксации изменений (т. е. сохранения созданных счетов в глобальном дереве состояния/Unitrie) пятьдесят раз за время тестирования. Сразу после размещения всех счетов мы используем два варианта поиска: 

  1. Поиск миллиона существующих счетов (все счета будут найдены).
  2. Поиск миллиона случайных счетов (на практике счета не будут найдены).

Наконец, тестирование позволяет точно установить время построения и поиска. Мы проводим тестирование неупорядоченного массива данных Java объемом 4 ГБ для интервала от 3 млн до 10 млн счетов, принадлежащих сторонним лицам. Мы учитываем отклонения, проводя тестирование шесть раз для каждого количества счетов. В следующей таблице показано среднее время построения и поиска для 3 млн, 6 млн и 9 млн счетов. CT означает «время построения», ELT — «время поиска существующих счетов», RLT — «время поиска случайных счетов». Время указано в секундах.

Таблица 1. Время построения и поиска

Наша реализация Unitrie спроектирована таким образом, чтобы максимально эффективно использовать память. В большинстве случаев это позволяет сократить время выполнения при использовании меньшего объема памяти. При этом варианте Unitrie проигрывает в сравнении с деревом Ethereum по показателю времени поиска, так как Unitrie медленнее в 1,2–1,8 раза. Но это окупается, если учитывать количество выделенных счетов. Unitrie может обрабатывать 9 млн счетов в памяти, в то время как реализация глобального состояния Besu едва достигает уровня в 6 млн счетов. Действительно, построение дерева с 6 млн счетов происходит в 1,47 раза медленнее, чем для классического дерева. Это показывает преимущества Besu относительно JVM для работы со счетами в пороговой области 6 млн.

Таблица 2. Пропускная способность построения и поиска

В таблице 2 суммируются результаты построения и поиска. Сокращение CTp означает пропускную способность при построении, которая измеряет количество обновлений глобального состояния Unitrie/Ethereum в секунду. Аналогично, ELTp и RLTp означают «поиск существующих счетов» и «пропускную способность поиска случайных счетов» (количество просмотров в секунду), соответственно. Как правило, Unitrie может выполнять от 55 до 80% операций, которые глобальное состояние Besu выполняет за тот же промежуток времени. В частности, из-за чистки мусора пропускная способность Unitrie в 1,5 раза превышает пропускную способность Besu в глобальном состоянии при добавлении 6 млн счетов. Кроме того, для такого количества счетов пропускная способность поиска Unitrie для одного миллиона случайных элементов соответствует глобальному состоянию Besu. 

Обратите внимание, что в худшем случае пропускная способность поиска составляет 67 тыс. просмотров в секунду (9 млн счетов, поиск существующих счетов). Чтобы убедиться, что это приемлемо, рассмотрим операцию BALANCE, стоимость которой составляет 400 ед. газа. Учитывая текущий предел газа для блока RSK на уровне 6,8 млн газа, в идеале мы можем выполнить в блоке максимум 6,8 млн/400 = 17 тыс. операций BALANCE, с чем даже при поиске со скоростью 67 тыс. операций в секунду Unitrie справится в течение 0,25 секунд при условии, что дерево сохраняется в памяти.

На рисунках 1, 2 и 3, соответственно, показано время построения и поиска существующих/случайных счетов при распределении счетов в диапазоне [3M, 10M]. Графики отображают центральную тенденцию и доверительные интервалы для каждой точки данных. Обратите внимание, что для неупорядоченного массива данных Java объемом 4 ГБ Unitrie начинает чистить мусор при достижении порогового значения для 9 млн счетов (сравните глобальное состояние Besu с пороговым значением 6 млн для очистки мусора). Кроме того, Unitrie использует более широкие доверительные интервалы. Это может быть связано с тем, что реализация Unitrie сохраняет общие пути в виде мягких ссылок, что приводит к недетерминированному добавлению накладных расходов на расчет путей во время выполнения.

Эксперимент 2: счета контракта

Unitrie закрывает каждый счет ключом, который формируется из хеша адреса счета. При формировании ключи счетов получают случайный префикс и имеют одинаковую длину, в результате чего получается среднее двоичное сбалансированное дерево со счетами в листьях. Это означает, что для N внешних счетов потребуется 2N — 1 узлов.

При рассмотрении счетов контракта емкость хранилища Unitrie обязательно уменьшается по сравнению с предыдущими расчетами. Это связано с тем, что Unitrie хранит код в качестве правого дочернего элемента соответствующего узла счета. Поэтому для N счетов, где α — это коэффициент отношения между счетами контракта

Рис. 1 Время построения для глобального состояния

Рис. 2 Время поиска (существующие счета)

Рис. 3 Время поиска (случайные счета)

и счетами внешнего владельца, размер Unitrie составляет (2 + α)N – 1 узлов. Это значение колеблется от 2N – 1 узлов, когда α = 0 (что включает первоначальный расчет для N счетов внешнего владельца), до 3N – 1 узлов, когда α = 1 (все счета являются счетами контракта). И наоборот, глобальное состояние Ethereum сохраняет код счета вне дерева, поэтому рассмотрение счетов контракта не затронет емкость хранилища дерева.

Чтобы оценить изменение емкости хранилища Unitrie, вызванное изменением значения α, мы запустили эталонный тест, распределяя как можно больше счетов в неупорядоченном массиве данных Java объемом 4 ГБ перед удалением. Мы проводили эксперимент для нескольких значений α в интервале [0,1]. В тестировании для кода счета используется код контракта ERC20 размером 2114 байт. На рисунке 4 показаны результаты, где значение α = 0,8 обозначено как точка, а Unitrie пересекает максимальную емкость глобального состояния Besu Ethereum. Также на этом рисунке показан рабочий диапазон Unitrie для неупорядоченного массива данных Java объемом 4 ГБ: Unitrie может сохранять где-то от 9,5М до 9M счетов внешнего владельца (α = 0) и 4,9M счетов контракта (α = 1).

Рис. 4 Изменение емкости хранилища, вызванное счетами контракта

Выводы

Unitrie позволяет увеличить объем памяти за счет повышения времени вычислений. На рисунках 2 и 3 показано, что время поиска Unitrie примерно в 1,5-2 раза больше для счетов в диапазоне [3 млн, 5 млн]. Однако это различие исчезнет в ситуации, когда глобальное состояние Besu заканчивается считыванием узлов с диска, потому что оно не позволяет сохранить в памяти достаточное глобальное состояние. В этом случае для времени поиска будет доминировать задержка дискового ввода-вывода. Наши эксперименты показывают, что Unitrie может хранить в неупорядоченном массиве данных Java объемом 4 ГБ глобальное состояние, состоящее из 9 млн счетов внешнего владельца, в то время как глобальное состояние Besu начинает ухудшаться с лимита счетов в 6 млн. В целом, Unitrie имеет больший потенциал для предотвращения дискового ввода-вывода, за счет в 1,8 раз более медленного поиска (в худшем случае) по сравнению с глобальным состоянием Besu.

При рассмотрении счетов контракта емкость хранилища определяется соотношением между счетами контракта и счетами внешнего владельца. Чем ниже значение α, тем больше узлов можно сохранить в памяти, что, в свою очередь, максимально повышает шансы избежать задержки дискового ввода-вывода. Второй эксперимент показывает, что объем памяти устройства Unitrie превышает Besu для α < 0,8.

Как оценивается α? По состоянию на ноябрь 2020 года, по данным Etherscan, в Mainnet насчитывается 80,28 млн уникальных счетов. На ту же дату количество счетов внешних владельцев составило 12,4 млн. Таким образом, α = 0,154. На момент написания статьи в Mainnet насчитывалось 94,14 млн уникальных адресов. Если предположить (что является нереальным), что каждый счет, созданный с ноября 2020 года, был контрактом, нам это даст завышенную верхнюю границу для α = 0,28. И даже это значение все еще находится в зоне комфорта Unitrie!

Приложение А: запуск экспериментов

Весь код эксперимента размещен в проекте Github rif-consensus-node, и для его использования требуется JDK 11 или более поздняя версия. После клонирования проекта вы можете запустить стресс-тест Unitrie или классический тест глобального состояния Ethereum, перейдя в корневую папку проекта и выполнив:

Где параметр accounts_to_create определяет количество создаваемых счетов, а параметр alpha_ratio соответствует соотношению между контрактом и внешними счетами, определенными во втором эксперименте.

Мы проводили эксперименты на MacBook Pro с двухъядерным процессором Intel i5 2,3 ГГц, 16 ГБ ОЗУ и SSD-накопителем на 250 ГБ. Все эксперименты проводились на неупорядоченном массиве данных JVM максимального объема 4 ГБ с использованием программы для очистки памяти G1.