Source de la réimpression de l'article : Techub News
Auteur : Tia, Techub News
La blockchain, en raison de son design décentralisé, a sacrifié l'efficacité, donc l'amélioration de la vitesse d'exécution a toujours été l'un des problèmes urgents à résoudre. La « couche d'exécution » de la blockchain est la partie clé qui traite chaque transaction et l'ajoute à la chaîne. Pour accélérer la capacité de traitement, améliorer la couche d'exécution est devenue l'une des stratégies clés, et l'exécution parallèle est une percée importante dans ce domaine.
Les blockchains traditionnelles traitent généralement les transactions de manière séquentielle, ce qui limite considérablement la vitesse des transactions, en particulier dans des réseaux à fort trafic, entraînant des congestions. Cependant, grâce à l'exécution parallèle, plusieurs transactions peuvent être traitées simultanément, augmentant ainsi considérablement l'efficacité d'exécution et allégeant la pression sur la chaîne.
Pour mieux comprendre ce qu'est le parallélisme, nous allons d'abord introduire l'exécution, en prenant l'exemple d'Ethereum dans le modèle PBS après la fusion, pour expliquer ce qu'est l'exécution tout en montrant la position de l'exécution dans le cycle de vie global des transactions.
Les étapes spécifiques de l'exécution des transactions
Les transactions entrent dans le pool mémoire et sont filtrées et triées : c'est la phase de prétraitement après la soumission de la transaction, qui comprend l'interaction entre le Mempool, le Searcher et le Builder, complétant le filtrage et le tri des transactions.
Le Builder construit le bloc (mais n'exécute pas) : le Builder organise les transactions rentables en un bloc pour finaliser le regroupement et le tri des transactions.
Le Proposer vérifie et soumet le bloc : une fois le bloc construit, le Builder envoie la proposition du bloc au Proposer. Le Proposer vérifie la structure du bloc et le contenu des transactions, puis soumet officiellement le bloc au réseau pour commencer l'exécution.
Exécution des transactions : une fois le bloc soumis, les nœuds exécutent les transactions dans le bloc une par une. C'est une phase clé de mise à jour de l'état, chaque transaction déclenchant des appels de contrats intelligents, des changements de solde de compte ou des modifications d'état.
Témoignage des témoins : les validateurs témoignent des résultats d'exécution du bloc et de la racine d'état, et les considèrent comme une confirmation finale. Cela garantit l'authenticité et la validité du bloc au niveau d'exécution et empêche les incohérences.
Synchronisation d'état : chaque nœud synchronise les résultats d'exécution du bloc (comme les soldes de compte, les mises à jour d'état de contrat, etc.) à son état local, après avoir exécuté chaque transaction, le nœud calcule et stocke une nouvelle racine d'état, utilisée comme état initial dans le bloc suivant.
Bien sûr, cela ne concerne que la synchronisation d'état des transactions par bloc, pour maintenir l'état le plus récent sur la chaîne, en général, les nœuds synchronisent les données bloc par bloc et vérifient continuellement les blocs et les états. Mais pour atteindre la finalité sous le mécanisme POS, les agrégateurs doivent agréger les signatures des témoins de chaque Slot en une signature complète, puis la transmettre au proposeur du prochain Slot, et les validateurs doivent confirmer l'état de tous les blocs dans cette Epoch en fonction du nombre de votes après une Epoch, formant ainsi un point de contrôle d'état de consensus temporaire. Après que deux Epoch consécutifs aient obtenu le soutien de la majorité des témoins, les blocs et les transactions atteindront la finalité.
Du point de vue du cycle de vie complet des transactions, l'exécution se produit après que le Proposer a vérifié la structure du bloc et le contenu des transactions envoyés par le Builder. Le processus d'exécution réel nécessite de traiter les transactions une par une, et de mettre à jour l'état des comptes ou des contrats correspondants. Une fois toutes les transactions exécutées, le Proposer calcule une nouvelle racine d'état (racine de Merkle), qui résume les résultats d'exécution de toutes les transactions du bloc actuel et l'état global final. En d'autres termes, le processus complet d'exécution d'un bloc comprend une série de calculs nécessaires pour transformer Ethereum de l'état précédent à l'état suivant, de l'exécution de chaque transaction au calcul de la racine de Merkle.
Exécution séquentielle
En opposition au parallélisme, l'exécution séquentielle est la méthode d'exécution la plus courante dans les blockchains. En général, les transactions sont exécutées progressivement dans l'ordre. Lorsque qu'une transaction est exécutée, Ethereum met à jour l'état du compte et les informations associées (comme les soldes, les données de stockage de contrat) dans l'arbre d'état du compte, générant un nouveau hachage d'état du compte. Une fois que tous les arbres d'état de compte ont été mis à jour, cela forme ce qu'on appelle la racine de Merkle d'état. Après avoir complété la racine de Merkle d'état, la racine de Merkle de la transaction et la racine de Merkle de reçu, l'en-tête du bloc subit un calcul de hachage, générant le hachage du bloc.
Dans ce contexte, l'ordre d'exécution des transactions est crucial. Étant donné que l'arbre de Merkle est un arbre binaire de hachage, les valeurs des racines de Merkle formées dans différents ordres seront différentes.
Exécution parallèle
Dans un environnement d'exécution parallèle, les nœuds tenteront de traiter les transactions dans le bloc de manière parallèle. Ce n'est pas une exécution transactionnelle séquentielle, mais plutôt l'attribution des transactions à différents « chemins d'exécution » pour qu'elles puissent s'exécuter simultanément. Grâce à l'exécution parallèle, le système peut traiter les transactions dans le bloc de manière plus efficace, augmentant ainsi le débit.
Une fois que toutes les transactions ont été exécutées, les nœuds résument les résultats d'exécution (c'est-à-dire les mises à jour d'état affectées par les transactions) et forment un nouvel état de bloc. Cet état sera ajouté à la blockchain, représentant l'état global le plus récent sur la chaîne.
Conflit d'état
Étant donné que le parallélisme traite les transactions sur différents chemins simultanément, l'un des principaux défis du parallélisme est le conflit d'état. Il peut y avoir plusieurs transactions qui lisent ou écrivent sur la même partie de données (état) sur la blockchain en même temps. Si cette situation n'est pas gérée correctement, cela peut entraîner des résultats d'exécution indéterminés. En raison de l'ordre de mise à jour de l'état, le résultat final des calculs peut également être différent. Par exemple,
Supposons qu'il y ait deux transactions, Transaction A et Transaction B, qui essaient toutes deux de mettre à jour le solde du même compte :
Transaction A : augmenter le solde du compte de 10.
Transaction B : augmenter le solde du compte de 20.
Le solde initial du compte est de 100.
Si nous exécutons en série, le résultat de l'ordre d'exécution est déterminé :
1. Exécuter d'abord la Transaction A, puis la Transaction B :
Le solde du compte augmente d'abord de 10, atteignant 110.
Puis augmente de 20, atteignant finalement 130.
2. Exécuter d'abord la Transaction B, puis la Transaction A :
Le solde du compte augmente d'abord de 20, atteignant 120.
Puis augmente de 10, atteignant finalement 130.
Dans ces deux ordres, le solde final est de 130, car le système garantit la cohérence de l'ordre d'exécution des transactions.
Mais dans un environnement d'exécution parallèle, les transactions A et B peuvent lire simultanément le solde initial de 100 et effectuer leurs propres calculs :
La transaction A lit un solde de 100, puis met à jour le solde à 110 après calcul.
La transaction B lit également un solde de 100, puis met à jour le solde à 120 après calcul.
Dans ce cas, étant donné que les transactions s'exécutent simultanément, le solde final est mis à jour uniquement à 120, et non à 130, car les opérations de la transaction A et de la transaction B se « chevauchent », entraînant un conflit d'état.
Ce type de problème de conflit d'état est souvent appelé « écrasement de données », c'est-à-dire que lorsque des transactions essaient de modifier simultanément les mêmes données, elles peuvent écraser les résultats de calcul des autres, entraînant un état final incorrect. Un autre type de conflit d'état qui peut survenir est l'incapacité à garantir l'ordre d'exécution. Étant donné que plusieurs transactions complètent leurs opérations à différents moments, cela peut entraîner différents ordres d'exécution. Des ordres différents peuvent conduire à des résultats de calcul différents, rendant ainsi le résultat indéterminé.
Pour éviter cette incertitude, les systèmes d'exécution parallèle blockchain introduisent généralement des mécanismes de détection des conflits et de retour en arrière, ou effectuent une analyse de dépendance des transactions à l'avance pour garantir qu'elles s'exécutent en parallèle sans affecter la cohérence finale de l'état.
Parallélisme optimiste et parallélisme déterministe
Il existe deux méthodes pour traiter les problèmes de conflits d'état potentiels : le parallélisme déterministe et le parallélisme optimiste. Ces deux modèles présentent des compromis en termes d'efficacité et de complexité de conception.
Le parallélisme déterministe nécessite une déclaration préalable d'accès à l'état, les validateurs ou séquenceurs vérifiant les accès d'état déclarés lors du tri des transactions. Si plusieurs transactions tentent d'écrire dans le même état, ces transactions seront marquées comme conflictuelles, évitant ainsi l'exécution simultanée. Les chaînes mettent en œuvre cette déclaration préalable de différentes manières, mais incluent généralement les méthodes suivantes :
À travers des contraintes de spécifications de contrat : les développeurs définissent directement la portée d'accès à l'état dans les contrats intelligents. Par exemple, le transfert de jetons ERC-20 nécessite d'accéder aux champs de solde de l'expéditeur et du destinataire.
À travers des déclarations de données structurées de transactions : ajout de champs spécifiques dans les transactions pour annoter l'accès à l'état.
À travers l'analyse par le compilateur : le compilateur des langages de haut niveau peut analyser statiquement le code du contrat pour générer automatiquement un ensemble d'accès à l'état.
À travers des déclarations forcées par des frameworks : certains frameworks exigent que les développeurs spécifient explicitement les états à accéder lors de l'appel de fonctions
Le parallélisme optimiste traite les transactions de manière optimiste en d'abord les exécutant, puis en réexécutant les transactions affectées dans l'ordre lorsqu'un conflit se produit. Pour éviter autant que possible les situations de conflit, le noyau du design du parallélisme optimiste est de faire des prévisions et des hypothèses rapides sur l'état à travers des données historiques, une analyse statique, etc. Cela signifie que le système suppose qu'opérations ou mises à jour d'état sont valides sans validation complète, évitant ainsi d'attendre tous les processus de validation pour améliorer les performances et le débit.
Bien que le parallélisme optimiste puisse éviter autant que possible les conflits par des prévisions et des hypothèses rapides sur l'état, certains défis restent inévitables, surtout lorsqu'il s'agit d'exécutions de contrats ou de transactions inter-chaînes. Si les conflits se produisent fréquemment, la réexécution peut ralentir considérablement les performances du système et augmenter la consommation des ressources de calcul.
Le parallélisme déterministe évite les éventuels conflits du parallélisme optimiste en vérifiant la dépendance d'état avant la transaction, mais cela nécessite de déclarer avec précision la dépendance d'état avant la soumission de la transaction, ce qui impose des exigences plus élevées aux développeurs, augmentant ainsi la complexité de mise en œuvre.
Dilemme de parallélisme EVM
La gestion des conflits d'état ne se limite pas seulement à la distinction entre déterministe et optimiste, dans le processus d'implémentation du parallélisme, il est également nécessaire de considérer la perspective de l'architecture de la base de données de la chaîne. Le problème des conflits d'état dans le parallélisme est particulièrement difficile dans l'EVM basé sur la structure de l'arbre de Merkle. Un arbre de Merkle est une structure de hachage hiérarchique, et chaque fois qu'une transaction modifie certaines données d'état, la racine de hachage de l'arbre de Merkle doit également être mise à jour. Ce processus de mise à jour est récursif, calculant couche par couche à partir des feuilles jusqu'à la racine. Étant donné que le hachage est irrécupérable, il n'est possible de calculer les couches supérieures qu'une fois que les modifications de données des couches inférieures sont terminées, cette caractéristique rend difficile la mise à jour parallèle.
Si deux transactions s'exécutent en parallèle et accèdent au même état (comme le solde du compte), cela entraînera des conflits sur les nœuds de l'arbre de Merkle. La résolution de ce conflit nécessite généralement des mécanismes de gestion des transactions supplémentaires pour garantir qu'un hachage racine cohérent puisse être obtenu dans plusieurs branches. Cela n'est pas facile à réaliser pour l'EVM, car il doit faire des compromis entre parallélisation et cohérence d'état.
Solutions parallèles non EVM
Solana
Contrairement à l'arbre d'état global d'Ethereum, Solana utilise un modèle de compte. Chaque compte est un espace de stockage indépendant, stocké dans le registre, évitant ainsi les problèmes de conflit de chemin.
Solana est un parallélisme déterministe. Dans Solana, chaque transaction nécessite de déclarer clairement les comptes qui seront accédés et les permissions d'accès requises (lecture seule ou lecture-écriture) au moment de la soumission. Ce design permet aux nœuds de blockchain d'analyser à l'avance les ressources nécessaires pour chaque transaction avant leur exécution. Étant donné que les transactions ont déjà clarifié toutes les dépendances de comptes avant de commencer l'exécution, les nœuds peuvent déterminer quelles transactions accéderont aux mêmes comptes, et quelles transactions peuvent être exécutées en toute sécurité en parallèle, réalisant ainsi une planification intelligente pour éviter les conflits, établissant ainsi la base pour la planification parallèle.
Étant donné que chaque transaction a déjà déclaré les comptes et les autorisations nécessaires avant l'exécution, Solana peut vérifier s'il existe des dépendances entre les comptes entre les transactions (modèle Sealevel). Si les transactions n'ont pas de comptes partagés en lecture et en écriture, le système peut les affecter à différents processeurs pour une exécution parallèle.
Aptos
La conception de l'exécution parallèle d'Aptos est très différente de celle d'Ethereum, apportant des innovations clés en termes d'architecture et de mécanismes, principalement en ce qui concerne le modèle de compte et le stockage d'état.
Ethereum nécessite des mises à jour fréquentes de l'arbre d'état global (MPT) lors de l'exécution des transactions. Tous les états des comptes et des contrats sont stockés dans un arbre d'état partagé, et chaque transaction doit accéder et mettre à jour une partie de cet arbre d'état. En revanche, Aptos divise les comptes en unités d'état indépendantes, chaque objet étant une paire clé-valeur indépendante, les objets pouvant exister indépendamment sans s'influencer mutuellement, ne se reliant que lorsqu'une relation de référence est clairement établie. Les objets n'ont pas de chemin d'arbre commun, évitant ainsi la concurrence de verrouillage, et pouvant être complètement parallèles.
La structure de données sous-jacente d'Aptos est l'Arbre de Merkle Jellyfish. L'état de chaque objet est finalement stocké dans le JMT, en tant que paire clé-valeur indépendante. Contrairement au MPT d'Ethereum, l'Arbre de Merkle Jellyfish est formé d'une structure d'arbre binaire complète, ce qui simplifie les chemins de stockage et de requête des nœuds, réduisant considérablement le temps de validation. De plus, chaque compte a une position fixe dans l'arbre, et les nœuds de l'arbre sont stockés indépendamment, permettant ainsi des mises à jour et des recherches parallèles pour plusieurs comptes.
Aptos est un parallélisme optimiste, il ne nécessite pas de fournir à l'avance toutes les dépendances des comptes déclarés. Pour cela, Aptos utilise Block-STM, qui utilise l'ordre de transaction préétabli pour estimer les dépendances, réduisant ainsi le nombre d'interruptions.
EVM parallèle
Comparé au parallélisme non EVM, l'EVM parallèle fait face à des défis techniques plus importants en matière de gestion des dépendances d'état, de détection des conflits, de gestion du gaz et de mécanismes de retour en arrière. Pour mieux comprendre cela, nous pouvons nous référer à comment certains projets EVM parallèles (comme Sui, Monad, Canto) résolvent ces problèmes.
Sui
Sui, comme Aptos, utilise également un modèle d'objet pour gérer l'état, considérant chaque objet (comme un compte, un état de contrat intelligent) comme une ressource indépendante, ces objets étant différenciés par des identifiants uniques. Lorsque des transactions impliquent différents objets, ces transactions peuvent être traitées en parallèle car elles n'opèrent pas sur le même état, évitant ainsi des conflits directs.
Bien que Sui utilise un modèle d'objet pour gérer l'état, pour être compatible avec l'EVM, l'architecture de Sui utilise une couche d'adaptation supplémentaire ou des mécanismes d'abstraction pour faire le pont entre le modèle d'objet et le modèle de compte EVM.
Dans Sui, la planification des transactions utilise une stratégie de parallélisme optimiste, supposant qu'il n'y a pas de conflits entre les transactions. Si un conflit se produit, le système utilise un mécanisme de retour en arrière pour restaurer l'état.
Sui utilise un modèle d'objet et une technique d'isolement d'état, ce qui permet d'éviter efficacement les problèmes de dépendance d'état. Chaque objet étant une ressource indépendante, différentes transactions peuvent s'exécuter en parallèle, augmentant ainsi le débit et l'efficacité. Mais ce modèle présente un compromis en termes de complexité du modèle d'objet et des coûts liés au mécanisme de retour en arrière. Si des conflits se produisent entre les transactions, cela nécessitera un retour en arrière sur certaines parties de l'état, augmentant ainsi la charge sur le système et pouvant affecter l'efficacité du traitement parallèle. Comparé aux systèmes parallèles non EVM (comme Solana), Sui nécessite davantage de ressources de calcul et de stockage pour maintenir une parallélisme efficace.
Monad
Comme Sui, Monad adopte également le parallélisme optimiste. Cependant, le parallélisme optimiste de Monad anticipe encore certaines transactions ayant des dépendances avant l'exécution des transactions, cette prévision étant principalement réalisée par l'analyse statique du code de Monad. Cette prévision nécessite un accès à l'état, et la manière dont l'état est stocké dans la base de données d'Ethereum rend l'accès à l'état très difficile. Afin de rendre le processus de lecture d'état plus efficace dans le parallélisme, Monad a également reconstruit la base de données.
L'arbre d'état Monad est divisé par partitions, chaque partition maintenant son propre sous-arbre d'état. Lors de la mise à jour, il suffit de modifier les fragments concernés, sans reconstruire l'ensemble de l'arbre d'état. Un tableau d'index d'état permet de localiser rapidement les états dans la partition, réduisant les interactions entre partitions.
Résumé
Le cœur du parallélisme est d'améliorer l'efficacité d'exécution de la couche d'exécution par l'exécution multi-chemins, et pour réaliser l'exécution multi-chemins, la chaîne doit procéder à une série de détections de conflits et mettre en place des mécanismes de retour en arrière pour garantir qu'elles s'exécutent en parallèle sans affecter la cohérence finale de l'état, et apporter certaines améliorations à la base de données.
Bien sûr, l'amélioration de l'efficacité de la couche d'exécution ne se limite pas à la parallélisation, l'optimisation de l'exécution peut également être réalisée en réduisant les opérations de lecture et d'écriture nécessaires à la base de données pour une transaction. L'amélioration de la vitesse de la chaîne englobe également un large éventail d'éléments, y compris l'amélioration de l'efficacité de la couche de consensus.
Chaque technologie a ses conditions de limitation spécifiques. Le parallélisme n'est qu'un des moyens d'améliorer l'efficacité, la décision d'utiliser cette technologie dépend également de sa convivialité pour les développeurs et de sa capacité à être mise en œuvre sans sacrifier la décentralisation, entre autres. L'accumulation de technologies n'est pas nécessairement meilleure, du moins pour Ethereum, le parallélisme n'est pas aussi attrayant, si l'on se concentre uniquement sur le point de vue de l'efficacité, intégrer le parallélisme n'est pas la solution optimale pour Ethereum, que ce soit en considérant la simplicité ou le plan centré sur Rollup d'Ethereum actuellement.