Статья перепечатана из: Techub News

Автор: Tia, Techub News

Блокчейн жертвует эффективностью из-за своего децентрализованного дизайна, поэтому повышение скорости выполнения всегда было одной из самых актуальных задач. «Уровень выполнения» блокчейна — это ключевая часть, которая обрабатывает каждую сделку и добавляет ее в цепь. Для ускорения обработки повышение производительности на уровне выполнения становится одной из основных стратегий, и параллельное выполнение является важным прорывом в этой области.

Традиционные блокчейны, как правило, используют последовательный подход для обработки сделок, что значительно ограничивает скорость сделок, особенно в насыщенных сетях, что может привести к перегрузке. Однако через параллельное выполнение можно одновременно обрабатывать несколько сделок, значительно увеличивая эффективность выполнения и уменьшая нагрузку на цепь.

Чтобы лучше понять, что такое параллельность, давайте сначала начнем с выполнения и возьмем Ethereum в режиме PBS после Merge в качестве примера, чтобы объяснить, что такое выполнение и одновременно показать, где выполнение находится в жизненном цикле сделки.

Конкретные этапы выполнения сделки

  1. Торговля попадает в пул памяти и проходит фильтрацию и сортировку: это предварительный этап обработки после подачи сделки, включающий взаимодействие между Mempool, Searcher и Builder, завершающее фильтрацию и сортировку сделок.

  2. Builder строит блок (но не выполняет): Builder упорядочивает выгодные сделки в блок, чтобы завершить упаковку и сортировку сделок.

  3. Предложитель проверяет и отправляет блок: после завершения построения блока Builder отправляет предложение блока Предложителю. Предложитель проверяет структуру блока и содержание сделок, а затем официально отправляет блок в сеть для начала выполнения.

  4. Выполнение сделок: после отправки блока узлы последовательно выполняют сделки внутри блока. Это ключевой этап обновления состояния, каждая сделка вызывает вызов смарт-контракта, изменение баланса аккаунта или изменение состояния.

  5. Свидетельство свидетелей: валидаторы подтверждают результаты выполнения блока и корень состояния и фиксируют это как окончательное подтверждение. Это гарантирует подлинность и действительность блока на уровне выполнения и предотвращает несоответствия.

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

Конечно, это только синхронизация состояния сделок по блокам. Чтобы поддерживать актуальное состояние на цепи, узлы обычно синхронизируют данные по блокам по одному и продолжают проверять блоки и состояния. Но чтобы достичь окончательности в механизме POS, агрегация должна объединить подписи свидетелей из каждого Slot в одну полную подпись и передать ее следующему Предложителю в Slot, а валидатор должен подтвердить состояние всех блоков в этом Epoch на основе количества голосов, формируя временную контрольную точку состояния консенсуса. Когда два последовательных Epoch получают поддержку большинства валидаторов, только тогда блоки и сделки достигают окончательности.

С точки зрения полного жизненного цикла сделки, выполнение происходит после того, как Предложитель проверяет структуру блока и содержание сделок, отправленных Builder. Фактический процесс выполнения требует последовательной обработки сделок и обновления соответствующих состояний аккаунтов или контрактов. После завершения всех сделок Предложитель вычисляет новый корень состояния (корень Меркла), который является резюме всех результатов выполнения сделок текущего блока и конечного глобального состояния. Проще говоря, полный процесс выполнения блока включает в себя серию вычислений, необходимых для преобразования Ethereum из предыдущего состояния в следующее состояние, от выполнения каждой сделки до вычисления корня Меркла.

Последовательное выполнение

Противоположностью параллельности является последовательное выполнение, которое является наиболее распространенным способом выполнения в блокчейне. Обычно сделки выполняются по порядку. После выполнения сделки Ethereum обновляет состояние аккаунта и связанную информацию (например, баланс, данные хранения контракта) в дереве состояния аккаунта, и создается новый хэш состояния аккаунта. После обновления всех деревьев состояния аккаунтов формируется корневой хэш дерева состояния, называемый корнем состояния Меркла. После завершения корней состояния Меркла, корней сделок Меркла и корней квитанций Меркла, заголовок блока проходит хеширование, создавая хэш блока.

При этом порядок выполнения сделок имеет решающее значение. Поскольку дерево Меркла представляет собой двоичное дерево хешей, корни Меркла, сформированные в различном порядке, будут различными.

Параллельное выполнение

В среде параллельного выполнения узлы пытаются обрабатывать сделки в блоке параллельно. Это не значит, что сделки выполняются последовательно одна за другой, а что сделки распределяются по различным «путям выполнения», чтобы они могли выполняться одновременно. Параллельное выполнение позволяет системе более эффективно обрабатывать сделки в блоке, увеличивая пропускную способность.

После завершения всех сделок узлы суммируют результаты выполнения (то есть обновления состояния, на которые повлияли сделки), создавая новое состояние блока. Это состояние будет добавлено в блокчейн, представляя собой новое глобальное состояние на цепи.

Конфликт состояния

Поскольку параллельность обрабатывает сделки одновременно по различным путям, одной из главных трудностей параллельности является конфликт состояния. То есть может возникнуть ситуация, когда несколько сделок в одно и то же время пытаются выполнить операции чтения или записи на одну и ту же часть данных (состояния) в блокчейне. Если это не будет правильно обработано, это может привести к неопределенности результата выполнения. Поскольку порядок обновления состояния различен, конечный результат вычислений также будет различным. Вот пример:

Предположим, есть две сделки, сделка A и сделка B, обе из которых пытаются обновить баланс одного и того же аккаунта:

  • Сделка A: увеличить баланс аккаунта на 10.

  • Сделка B: увеличить баланс аккаунта на 20.

Начальный баланс аккаунта составляет 100.

Если мы выполняем последовательно, результат порядка выполнения определен:

1. Сначала выполняется сделка A, затем сделка B:

  • Баланс аккаунта сначала увеличивается на 10, становится 110.

  • Затем увеличивается на 20, в конечном итоге становится 130.

2. Сначала выполняется сделка B, затем сделка A:

  • Баланс аккаунта сначала увеличивается на 20, становится 120.

  • Затем увеличивается на 10, в конечном итоге становится 130.

В обоих этих порядках конечный баланс составляет 130, поскольку система гарантирует согласованность порядка выполнения сделок.

Но в среде параллельного выполнения сделки A и B могут одновременно считывать начальный баланс 100 и выполнять свои вычисления:

  1. Сделка A считывает баланс 100 и обновляет его до 110 после вычисления.

  2. Сделка B также считывает баланс 100 и обновляет его до 120 после вычисления.

В этом случае, поскольку сделки выполняются одновременно, конечный баланс обновляется только до 120, а не до 130, поскольку операции сделок A и B «перекрывают» результаты друг друга, создавая конфликт состояния.

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

Чтобы избежать этой неопределенности, системы параллельного выполнения блокчейна обычно вводят механизмы обнаружения конфликтов и отката, либо заранее проводят анализ зависимостей сделок, чтобы гарантировать их параллельное выполнение без ущерба для окончательной согласованности состояния.

Оптимистическая параллельность и определяемая параллельность

Существует два способа решения возможных конфликтов состояния: определяемая параллельность и оптимистическая параллельность. Эти два режима имеют свои компромиссы в эффективности и сложности дизайна.

Определяемая параллельность требует предварительного объявления доступа к состоянию; валидатор или последователь проверяет объявленный доступ к состоянию при сортировке сделок. Если несколько сделок пытаются записать одно и то же состояние, эти сделки помечаются как конфликтующие, чтобы избежать параллельного выполнения. Конкретные реализации предварительного объявления доступа к состоянию различаются в разных цепях, но обычно включают в себя несколько следующих способов:

  • Через контрактные нормы: разработчики прямо определяют диапазон доступа состояния в смарт-контракте. Например, передача токенов ERC-20 требует доступа к полям баланса отправителя и получателя.

  • Через структурированные данные сделки: добавление специальных полей в сделку для указания доступа к состоянию.

  • Через анализатор компилятора: компиляторы высокоуровневых языков могут статически анализировать код контракта и автоматически генерировать наборы доступа к состоянию.

  • Через обязательное объявление фреймворков: некоторые фреймворки требуют от разработчиков явно указывать состояние, к которому необходимо обратиться при вызове функции.

Оптимистическая параллельность предполагает предварительную обработку сделок, и когда происходит конфликт, затронутые сделки переисполняются по порядку. Чтобы максимально избежать конфликтов, основная идея оптимистической параллельности заключается в быстром предсказании и гипотезах о состоянии с помощью исторических данных, статического анализа и т.д. То есть система предполагает, что некоторые операции или обновления состояния являются действительными без полной проверки, чтобы избежать ожидания всех процессов проверки и таким образом повысить производительность и пропускную способность.

Хотя оптимистическая параллельность может избегать конфликтов через быструю предварительную оценку и гипотезы о состоянии, все же существуют некоторые неизбежные проблемы, особенно при выполнении контрактов или кросс-цепочных сделок. Если конфликты возникают часто, повторное выполнение может значительно замедлить производительность системы и увеличить потребление вычислительных ресурсов.

Определяемая параллельность избегает конфликтов, которые могут возникнуть при оптимистической параллельности, проверяя зависимости состояния перед сделкой, но поскольку требуется точно объявить зависимости состояния до подачи сделки, это предъявляет более высокие требования к разработчикам и увеличивает сложность реализации.

Параллельная проблема EVM

Подход к конфликтам состояния может различаться между определяемой и оптимистической, в процессе реализации параллельности также необходимо учитывать архитектуру базы данных цепи. Проблема конфликтов состояния в EVM под архитектурой дерева Меркла особенно сложна. Дерево Меркла — это иерархическая хеш-структура, и после того, как сделка изменяет определенные данные состояния, корневой хэш дерева Меркла также необходимо обновить. Этот процесс обновления рекурсивный, начиная с листовых узлов и постепенно рассчитывая до корневого узла. Поскольку хеши необратимы, то верхний уровень можно вычислить только после завершения изменений нижнего уровня, эта особенность делает его трудным для параллельного обновления.

Если две сделки выполняются параллельно и обращаются к одному и тому же состоянию (например, баланс аккаунта), это приведет к конфликту узлов дерева Меркла. А решение такого конфликта обычно требует дополнительных механизмов управления транзакциями, чтобы гарантировать, что в нескольких ветвях можно получить согласованный корневой хэш. Это не легко реализовать для EVM, поскольку ему необходимо делать компромиссы между параллелизацией и согласованностью состояния.

Непараллельные решения EVM

Solana

В отличие от глобального дерева состояния Ethereum, Solana использует модель аккаунтов. Каждый аккаунт представляет собой независимое пространство хранения, сохраненное в книге, что предотвращает проблемы с конфликтами путей.

Solana — это определяемая параллельность. В Solana каждая сделка должна четко объявить аккаунты, к которым она будет обращаться, и необходимые разрешения (только для чтения или чтения и записи) при подаче. Этот дизайн позволяет узлам блокчейна заранее анализировать ресурсы, которые необходимо использовать, до того, как сделка будет выполнена. Поскольку сделки заранее объявляют все зависимости аккаунтов, узлы могут определить, какие сделки будут обращаться к одному и тому же аккаунту, а какие сделки могут безопасно выполняться параллельно, что позволяет реализовать умное планирование, избежать конфликтов и тем самым создать основу для параллельного выполнения.

Поскольку каждая сделка заранее объявляет аккаунты и разрешения, которые необходимо использовать, Solana может проверять наличие зависимостей между сделками (модель Sealevel). Если у сделок нет общих читаемых и записываемых аккаунтов, система может распределить их по различным процессорам для параллельного выполнения.

Aptos

Дизайн параллельного выполнения Aptos значительно отличается от Ethereum, он делает некоторые ключевые инновации в архитектуре и механизмах, в основном касающихся модели аккаунтов и хранения состояния.

Ethereum часто требует обновления глобального дерева состояния (MPT) при выполнении сделок. Все состояния аккаунтов и контрактов хранятся в общем дереве состояния, и любая сделка требует доступа к и обновления части этого дерева состояния. Однако Aptos разделяет аккаунты на независимые единицы состояния, где каждый объект является независимой парой ключ-значение, и объекты могут существовать независимо друг от друга, связываясь только при явной ссылке. Между объектами нет общих путей в дереве, что предотвращает конкуренцию за блокировки и позволяет полностью параллельное выполнение.

Основная структура данных Aptos — это Jellyfish Merkle Tree. Состояние каждого объекта в конечном итоге хранится в JMT в виде независимой пары ключ-значение. В отличие от MPT Ethereum, Jellyfish Merkle Tree имеет структуру полного двоичного дерева, что упрощает пути хранения и запроса узлов, значительно снижая время верификации. Кроме того, каждый аккаунт занимает фиксированное место в дереве, и узлы дерева хранятся независимо, что позволяет параллельно обновлять и искать несколько аккаунтов.

Aptos — это оптимистическая параллельность, она не требует предварительного предоставления всех объявленных зависимостей аккаунтов. Для этого Aptos использует Block-STM, который использует установленный порядок сделок для оценки зависимостей, чтобы снизить количество прерываний.

Параллельный EVM

По сравнению с непараллельными EVM, параллельный EVM сталкивается с большими техническими трудностями при решении проблем зависимости состояния, обнаружения конфликтов, управления газом и механизмов отката. Чтобы лучше понять это, мы можем рассмотреть, как некоторые проекты параллельного EVM (такие как Sui, Monad, Canto) решают эти проблемы.

Sui

Sui, как и Aptos, также использует объектную модель для обработки состояния, принимая каждый объект (например, аккаунт, состояние смарт-контракта) в качестве независимого ресурса, которые различаются по уникальному идентификатору объекта. Когда сделки касаются разных объектов, эти сделки могут обрабатываться параллельно, так как они работают с разными состояниями и не вызывают прямых конфликтов.

Хотя Sui использует объектную модель для управления состоянием, для совместимости с EVM архитектура Sui использует дополнительные адаптационные уровни или абстрактные механизмы, чтобы соединить объектную модель и модель аккаунтов EVM.

В Sui планирование транзакций использует стратегию оптимистической параллельности, предполагая, что между транзакциями нет конфликтов. Если конфликт возникает, система использует механизмы отката для восстановления состояния.

Sui использует объектную модель и технологии изоляции состояния, что эффективно предотвращает проблемы зависимости состояния. Каждый объект является независимым ресурсом, и разные сделки могут выполняться параллельно, что увеличивает пропускную способность и эффективность. Но этот метод имеет свои компромиссы в виде сложности объектной модели и накладных расходов на механизмы отката. Если между сделками возникают конфликты, требуется откат некоторых состояний, что увеличивает нагрузку на систему и может повлиять на эффективность параллельной обработки. В отличие от непараллельных систем EVM (таких как Solana), Sui требует больше вычислительных и хранилищных ресурсов для поддержания эффективной параллельности.

Monad

Как и Sui, Monad также использует оптимистическую параллельность. Однако оптимистическая параллельность Monad все же будет предсказывать некоторые зависимые сделки перед фактическим выполнением сделок, предсказание осуществляется в основном с помощью статического анализатора кода Monad. Предсказание требует доступа к состоянию, в то время как способ хранения состояния в базе данных Ethereum затрудняет доступ к состоянию, чтобы сделать процесс чтения состояния более эффективным, Monad также перестраивает базу данных.

Дерево состояния Monad разделено на партиции, каждая из которых поддерживает свое собственное поддерево состояния. При обновлении необходимо изменить только соответствующий фрагмент, без необходимости перестраивать все дерево состояния. Быстрое локализование состояния в партиции осуществляется через индекс состояния, что уменьшает взаимодействие между партициями.

Резюме

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

Конечно, улучшение эффективности уровня выполнения не ограничивается только параллельностью; оптимизация этапа выполнения также может быть достигнута путем снижения количества операций чтения и записи, необходимых для одной сделки. В то время как повышение скорости всей цепи охватывает более широкий спектр, включая повышение эффективности уровня консенсуса.

За каждой технологией стоят определенные ограничения. Параллельность — это лишь один из способов повышения эффективности, и окончательное решение о том, использовать ли эту технологию, также необходимо рассмотреть с точки зрения удобства для разработчиков и возможности завершить это без жертвы децентрализации и т.д. Накопление технологий не всегда лучше, по крайней мере, для Ethereum параллельность не так привлекательна, если рассматривать только повышение эффективности, добавление параллельности для Ethereum не является оптимальным решением, независимо от того, рассматриваем ли мы простоту или дорожную карту Ethereum с центром на Rollup.