Autor: Tia, Techub News
Blockchain-ul sacrifică eficiența din cauza designului său descentralizat, astfel că creșterea vitezei de execuție a fost întotdeauna una dintre problemele urgente ce trebuie rezolvate. „Stratul de execuție” al blockchain-ului este partea esențială care se ocupă cu procesarea fiecărei tranzacții și adăugarea acesteia în lanț. Pentru a îmbunătăți capacitatea de procesare, îmbunătățirea stratului de execuție a devenit una dintre strategiile cheie, iar execuția paralelă este o importantă descoperire în acest sens.
Blockchain-urile tradiționale procesează de obicei tranzacțiile în mod serial, ceea ce limitează semnificativ viteza de execuție, în special în rețelele cu tranzacții dense care pot provoca congestie. Cu toate acestea, prin execuția paralelă, mai multe tranzacții pot fi procesate simultan, crescând astfel semnificativ eficiența execuției și reducând presiunea asupra lanțului.
Pentru a înțelege mai bine ce este execuția paralelă, vom începe prin a introduce execuția, folosind Ethereum în modul PBS după fuzionare ca exemplu pentru a explica ce este execuția și a arăta locul execuției în întregul ciclu de viață al tranzacțiilor.
Etapele specifice ale execuției tranzacției
Tranzacțiile intră în mempool și sunt filtrate și sortate: aceasta este etapa de preprocesare a tranzacțiilor după ce au fost trimise, incluzând interacțiunile între Mempool, Searcher și Builder, pentru a finaliza filtrarea și sortarea tranzacțiilor.
Builder-ul construiește blocuri (dar nu execută): Builder-ul aranjează tranzacțiile profitabile într-un bloc pentru a finaliza împachetarea și sortarea tranzacțiilor.
Proposer-ul verifică și trimite blocul: După ce blocul este construit, Builder-ul trimite propunerea blocului către Proposer. Proposer-ul verifică structura blocului și conținutul tranzacțiilor, apoi trimite oficial blocul în rețea pentru a începe execuția.
Execuția tranzacțiilor: După ce blocul este trimis, nodurile execută tranzacțiile din interiorul blocului, de la o tranzacție la alta. Aceasta este etapa cheie a actualizării stării, fiecare tranzacție declanșând apeluri de contract inteligent, schimbări ale soldului contului sau modificări ale stării.
Martorul atestă: Validatorul atestă rezultatul execuției blocului și rădăcina stării, considerându-le ca o confirmare finală. Aceasta asigură autenticitatea și validitatea blocului în stratul de execuție și previne inconsistențele.
Sincronizarea stării: Fiecare nod își va sincroniza rezultatul execuției blocului (cum ar fi soldurile contului, actualizările stării contractului etc.) cu propria stare locală, după fiecare tranzacție executată, nodul calculează și stochează o nouă rădăcină de stare pentru a fi utilizată ca stare inițială în următorul bloc.
Desigur, aceasta este doar sincronizarea stării tranzacției pe baza blocului, pentru a menține cea mai recentă stare de pe lanț, în general, nodurile vor sincroniza datele bloc cu bloc și vor continua să valideze blocurile și starea. Dar pentru a atinge finalitatea în mecanismul POS, agregatorii trebuie să agregheze semnăturile martorilor din fiecare Slot într-o semnătură completă și să le transmită Proposer-ului următorului Slot, iar validatorul trebuie să confirme starea tuturor blocurilor din acest Epoch pe baza numărului de voturi după un Epoch, formând un punct de control temporar al stării consensuale. Numai după ce două Epoch-uri consecutive primesc suportul majorității validatorilor, blocul și tranzacțiile vor atinge finalitatea.
Privind întregul ciclu de viață al tranzacției, execuția are loc după ce Proposer-ul validează structura și conținutul tranzacției trimise de Builder. Procesul efectiv de execuție necesită procesarea fiecărei tranzacții și actualizarea stării corespunzătoare a contului sau contractului. După ce toate tranzacțiile au fost executate, Proposer-ul va calcula o nouă rădăcină de stare (rădăcina Merkle), care este un rezumat al rezultatelor execuției tuturor tranzacțiilor din blocul curent și a stării globale finale. Pe scurt, întregul proces de execuție a blocului implică o serie de calcule necesare pentru a transforma Ethereum dintr-o stare anterioară într-o stare ulterioară, de la execuția fiecărei tranzacții până la calculul rădăcinii Merkle.
Execuție secvențială
În contrast cu execuția paralelă, execuția secvențială este modul de execuție mai comun în prezent pe blockchain. De obicei, tranzacțiile sunt executate pas cu pas în ordinea în care sunt primite. Odată ce o tranzacție a fost executată, Ethereum va actualiza starea contului și informațiile relevante (de exemplu, solduri, date de stocare a contractului) în arborele stării contului, generând un nou hash de stare a contului. După ce toate arborile de stare ale contului sunt actualizate, se va forma un nod de rădăcină a arborelui de stare numit rădăcina Merkle a stării. După finalizarea rădăcinilor Merkle de stare, de tranzacție și a chitanței, antetul blocului va fi supus unui calcul hash pentru a genera hash-ul blocului.
În acest context, ordinea de execuție a tranzacției este crucială. Deoarece copacul Merkle este un arbore binar de hash, valorile rădăcinii Merkle formate în ordini diferite vor fi diferite.
Execuție paralelă
În mediul de execuție paralelă, nodurile vor încerca să proceseze tranzacțiile din bloc în mod paralel. Nu sunt executate pas cu pas în ordine, ci tranzacțiile sunt repartizate pe diferite „căi de execuție” pentru a putea fi executate simultan. Prin execuția paralelă, sistemul poate gestiona mai eficient tranzacțiile din bloc, sporind astfel capacitatea de procesare.
După finalizarea execuției tuturor tranzacțiilor, nodul va compune rezultatele execuției (adică actualizările stării afectate de tranzacții) și va forma o nouă stare a blocului. Această stare va fi adăugată în blockchain, reprezentând cea mai recentă stare globală de pe lanț.
Conflict de stare
Deoarece paralelismul va trata tranzacțiile simultan pe diferite căi, o mare provocare a paralelismului este conflictul de stare. Aceasta se referă la situații în care mai multe tranzacții pot citi sau scrie simultan în aceeași parte a datelor (stării) din blockchain. Dacă această situație nu este gestionată corespunzător, va duce la rezultate de execuție incerte. Deoarece ordinea de actualizare a stării este diferită, rezultatul final al calculului va fi, de asemenea, diferit. De exemplu,
Să presupunem că avem două tranzacții, tranzacția A și tranzacția B, ambele încercând să actualizeze soldul aceleași conturi:
Tranzacția A: crește soldul contului cu 10.
Tranzacția B: crește soldul contului cu 20.
Soldul inițial al contului este de 100.
Dacă executăm în mod serial, rezultatul ordinii de execuție este determinat:
1. Executați întâi tranzacția A, apoi tranzacția B:
Soldul contului crește întâi cu 10, devenind 110.
Apoi crește cu 20, ajungând în final la 130.
2. Executați întâi tranzacția B, apoi tranzacția A:
Soldul contului crește întâi cu 20, devenind 120.
Apoi crește cu 10, ajungând în final la 130.
În aceste două ordine, soldul final este 130, deoarece sistemul asigură consistența ordinii execuției tranzacțiilor.
Dar în mediu de execuție paralelă, tranzacția A și tranzacția B ar putea citi simultan soldul inițial de 100 și efectua calculele proprii:
Tranzacția A citește soldul de 100 și, după calcul, actualizează soldul la 110.
Tranzacția B citește de asemenea soldul de 100 și, după calcul, actualizează soldul la 120.
În această situație, din cauza execuției simultane a tranzacțiilor, soldul final este actualizat doar la 120, nu la 130, deoarece operațiunile tranzacției A și B „se suprapun”, generând un conflict de stare.
Aceste probleme de conflict de stare sunt adesea numite „suprapunere de date”, adică atunci când tranzacțiile încearcă să modifice simultan aceleași date, rezultatele calculelor lor ar putea să se suprapună, ducând la o stare finală incorectă. O altă problemă posibilă a conflictelor de stare ar putea fi imposibilitatea de a garanta ordinea execuției. Deoarece mai multe tranzacții finalizează operațiunile la momente diferite, se pot forma ordini diferite de execuție. Ordinea diferită poate conduce la rezultate de calcul diferite, făcând rezultatul incert.
Pentru a evita această incertitudine, sistemele de execuție paralelă pe blockchain vor introduce de obicei unele mecanisme de detectare a conflictelor și de revenire, sau vor analiza anticipat dependențele tranzacțiilor pentru a se asigura că acestea sunt executate în paralel fără a afecta consistența finală a stării.
Execuție optimistă și execuție deterministă
Există două metode pentru a aborda problemele posibile de conflict de stare: execuția deterministă și execuția optimistă. Aceste două modele au compromisuri în ceea ce privește eficiență și complexitate de design.
Execuția deterministă necesită declararea prealabilă a accesului la stare, validatorul sau sequencer-ul va verifica declarațiile de acces la stare în timpul sortării tranzacțiilor. Dacă mai multe tranzacții încearcă să scrie în aceeași stare, acestea vor fi marcate ca fiind în conflict, evitând execuția simultană. Diferitele lanțuri implementează forme diferite de declarație prealabilă a accesului la stare, dar de obicei includ următoarele metode:
Prin reglementări contractuale: dezvoltatorii stabilesc direct domeniul de acces la stare în contractele inteligente. De exemplu, transferul de token-uri ERC-20 necesită acces la câmpurile de sold ale expeditorului și destinatarului.
Prin declararea datelor structurate în tranzacție: adăugând câmpuri specializate în tranzacție pentru a marca accesul la stare.
Prin analiza compilatorului: compilatoarele limbajelor de nivel înalt pot analiza statica codul contractului, generând automat un set de acces la stare.
Declarație impusă prin cadru: anumite cadre necesită ca dezvoltatorii să specifice în mod explicit starea la care doresc să aibă acces atunci când apelează funcțiile
Execuția optimistă va procesa în mod optimist tranzacțiile întâi, iar când apare un conflict, tranzacțiile afectate vor fi re-executate în ordinea corectă. Pentru a evita apariția conflictelor, esența designului optimist este de a prezice rapid starea prin date istorice, analize statice etc. Adică sistemul, fără a valida complet, presupune că anumite operațiuni sau actualizări de stare sunt valide, încercând să evite așteptarea tuturor proceselor de validare pentru a îmbunătăți performanța și capacitatea de procesare.
Deși execuția optimistă poate evita conflictele prin evaluări rapide și ipoteze ale stării, există totuși provocări inevitabile, în special în ceea ce privește execuția contractelor sau tranzacțiile inter-chain. Dacă conflictele apar frecvent, re-executarea poate încetini semnificativ performanța sistemului și poate crește consumul de resurse de calcul.
Execuția deterministă evită conflictele posibile prin verificarea dependențelor de stare înainte de tranzacție, dar pentru a declara cu exactitate dependențele de stare înainte de a trimite tranzacția, aceasta impune cerințe mai mari dezvoltatorilor, crescând astfel complexitatea implementării.
Dilema paralelismului EVM
Conflictele de stare nu sunt doar o chestiune de determinism și optimism, ci necesită și o considerație din punctul de vedere al arhitecturii bazelor de date pe lanț în timpul procesului de implementare a paralelismului. Problema conflictului de stare în paralel este deosebit de dificilă în EVM-ul sub arhitectura copacului Merkle. Copacul Merkle este o structură de hash ierarhică, iar după fiecare modificare a datelor de stare printr-o tranzacție, valoarea hash a rădăcinii copacului Merkle trebuie, de asemenea, actualizată. Acest proces de actualizare este recursiv, calculând din frunzele copacului în sus, strat cu strat, până la rădăcină. Deoarece hash-ul este ireversibil, adică poate fi calculat la nivelul superior doar după ce modificarea datelor de la nivelul inferior este completă, această caracteristică face actualizarea paralelă foarte dificilă.
Dacă două tranzacții sunt executate în paralel și accesează aceeași stare (cum ar fi soldul contului), va apărea un conflict la nodurile copacului Merkle. Rezolvarea acestui conflict necesită de obicei mecanisme suplimentare de gestionare a tranzacțiilor pentru a asigura valori hash ale rădăcinii consistente în ramuri multiple. Acest lucru nu este ușor de realizat pentru EVM, deoarece trebuie să facă un compromis între paralelism și consistența stării.
Soluții de paralelism non-EVM
Solana
Spre deosebire de arborele de stare global al Ethereum, Solana folosește un model de cont. Fiecare cont este un spațiu de stocare independent, stocat în registru, evitând astfel problemele de conflict de căi.
Solana este un sistem de paralelism determinist. În Solana, fiecare tranzacție trebuie să declare explicit conturile pe care le va accesa și permisiunile necesare (doar citire sau citire și scriere) în momentul în care este trimisă. Acest design permite nodurilor blockchain să analizeze în prealabil resursele de care fiecare tranzacție va avea nevoie în timpul execuției. Deoarece tranzacțiile au declarat toate relațiile de dependență ale conturilor înainte de a începe execuția, nodurile pot judeca ce tranzacții vor accesa aceleași conturi și ce tranzacții pot fi executate în siguranță în paralel, realizând astfel programare inteligentă pentru a evita conflictele, stabilind astfel baza pentru programarea paralelă.
Deoarece fiecare tranzacție a declarat în prealabil conturile și permisiunile necesare, Solana poate verifica dacă există dependențe între tranzacții (modelul Sealevel). Dacă nu există conturi comune de citire și scriere între tranzacții, sistemul le poate repartiza pe diferite procesoare pentru a fi executate în paralel.
Aptos
Designul de execuție paralelă al Aptos este foarte diferit de cel al Ethereum, având unele inovații cheie în arhitectură și mecanisme, evidențiate în principal prin modelul de cont și stocarea stării.
Ethereum necesită actualizări frecvente ale arborelui de stare global (MPT) în timpul executării tranzacțiilor. Toate stările conturilor și contractelor sunt stocate într-un arbore de stare comun, iar orice tranzacție trebuie să acceseze și să actualizeze o parte din acest arbore de stare. În schimb, Aptos împarte conturile în unități de stare independente, fiecare obiect fiind o pereche cheie-valoare independentă, unde obiectele pot exista independent fără a se influența reciproc, asociindu-se doar în cazul unei relații de referință clare. Obiectele nu au căi comune în arbore, nu există competiție pentru blocare, astfel încât totul poate fi complet paralel.
Structura de date de bază a Aptos este Jellyfish Merkle Tree. Starea fiecărui obiect este în cele din urmă stocată în JMT, ca pereche cheie-valoare independentă. Spre deosebire de MPT-ul Ethereum, Jellyfish Merkle Tree este o structură complet binară, ceea ce simplifică calea de stocare și calea de interogare a nodurilor, reducând semnificativ timpul de validare. Fiecare cont are o poziție fixă în copac, iar nodurile din copac sunt stocate independent, permițând actualizări și căutări paralele pentru mai multe conturi.
Aptos este optimist, nefiind necesară furnizarea prealabilă a tuturor dependențelor conturilor declarate. În acest scop, Aptos utilizează Block-STM, care va folosi ordinea presetată a tranzacțiilor pentru a estima dependențele, reducând astfel numărul de întreruperi.
EVM paralel
Comparativ cu paralelismul non-EVM, EVM paralel se confruntă cu dificultăți tehnice mai mari în gestionarea dependențelor de stare, detectarea conflictelor, gestionarea gazului și mecanismele de revenire. Pentru a înțelege mai bine acest lucru, putem consulta modul în care unele proiecte EVM paralele (cum ar fi Sui, Monad, Canto) abordează aceste probleme.
Sui
Sui, la fel ca Aptos, utilizează de asemenea un model de obiecte pentru a gestiona starea, folosind fiecare obiect (de exemplu, starea contului, contractul inteligent) ca resursă independentă, iar aceste obiecte sunt distinse prin identificatori unici. Când tranzacțiile implică obiecte diferite, aceste tranzacții pot fi procesate în paralel, deoarece operează asupra unor stări diferite, fără a genera conflicte directe.
Deși Sui folosește un model de obiecte pentru a gestiona starea, pentru a fi compatibil cu EVM, arhitectura Sui utilizează un strat de adaptare suplimentar sau mecanisme abstracte pentru a conecta modelul de obiecte cu modelul de cont EVM.
În Sui, programarea tranzacțiilor utilizează strategia de execuție optimistă, presupunând că nu există conflicte între tranzacții. Dacă apar conflicte, sistemul va utiliza mecanismul de revenire pentru a restabili starea.
Sui a folosit modelul de obiecte și tehnica de izolare a stării, eficient evitând problemele de dependență a stării. Fiecare obiect acționează ca o resursă independentă, iar tranzacțiile diferite pot fi executate în paralel, sporind astfel capacitatea de procesare și eficiența. Totuși, acest mod are un compromis în complexitatea modelului de obiecte și costul mecanismelor de revenire. Dacă apar conflicte între tranzacții, va fi necesar să se revină asupra unei părți din stare, ceea ce va crește povara sistemului și ar putea afecta eficiența procesării paralele. Spre deosebire de sistemele non-EVM (cum ar fi Solana), Sui necesită mai multe resurse de calcul și stocare pentru a menține o paralelism eficient.
Monad
La fel ca Sui, Monad adoptă de asemenea execuția optimistă. Dar execuția optimistă a Monad va prezice, înainte de execuția efectivă a tranzacției, anumite tranzacții cu dependențe, predicția fiind realizată prin analiza statică a codului Monad. Predicția necesită acces la stare, iar modul în care Ethereum stochează starea în baza de date face accesul la stare foarte dificil; pentru a face procesul de citire a stării mai eficient în execuția paralelă, Monad a refăcut și baza de date.
Copacul de stare Monad este împărțit pe secțiuni, fiecare secțiune menținând propriul său sub-arbore de stare. La actualizare, este suficient să modificați fragmentele relevante, fără a necesita reconstrucția întregului copac de stare. Printr-o tabelă de indexare a stării, se poate localiza rapid starea dintr-o secțiune, reducând interacțiunile între secțiuni.
Sumar
Paralelismul se concentrează pe creșterea eficienței execuției straturilor prin execuția pe mai multe căi, iar pentru a realiza execuția pe mai multe căi, lanțul trebuie să implementeze o serie de mecanisme de detectare a conflictelor și de revenire pentru a asigura execuția paralelă fără a afecta consistența finală a stării și să facă anumite îmbunătățiri ale bazei de date.
Desigur, îmbunătățirea eficienței stratului de execuție nu se limitează doar la paralelism, optimizarea procesului de execuție poate fi realizată și prin reducerea operațiunilor de citire și scriere necesare pentru o singură tranzacție în baza de date. Îmbunătățirea vitezei întregului lanț implică un domeniu și mai larg, inclusiv și îmbunătățiri ale eficienței stratului de consens.
Fiecare tehnologie are condiții specifice de limitare. Paralelismul este doar una dintre modalitățile de îmbunătățire a eficienței, iar decizia finală de a folosi această tehnologie trebuie să țină cont de prietenia față de dezvoltatori, de capacitatea de a realiza acest lucru fără a sacrifica descentralizarea etc. Agregarea tehnologiilor nu este întotdeauna mai bună, mai ales în cazul Ethereum, unde paralelismul nu este atât de atractiv; din perspectiva îmbunătățirii eficienței, adăugarea paralelismului pentru Ethereum nu este soluția optimă, fie din considerente de simplitate, fie din perspectiva foii de parcurs axate pe Rollup-ul Ethereum.