저자: 미스티 문, 긱웹3
우리 모두 알다시피 EVM은 이더의 "실행 엔진" 및 "스마트 컨트랙트 실행 환경"으로 자리매김하고 있습니다. 우리 모두 알다시피 EVM은 이더리움의 '실행 엔진'이자 '스마트 컨트랙트 실행 환경'으로 자리 잡고 있으며, 이더리움의 가장 중요한 핵심 구성 요소 중 하나라고 할 수 있습니다. 퍼블릭 체인은 수천 개의 노드로 구성된 개방형 네트워크로, 노드마다 하드웨어 파라미터가 크게 다릅니다. 스마트 컨트랙트가 여러 노드에서 동일한 결과로 실행되고 일관성을 충족하려면 여러 장치에서 동일한 환경을 구축할 수 있는 방법을 찾아야 하는데, 가상 머신을 통해 이를 구현할 수 있습니다.
이더리움의 가상 머신인 EVM은 다양한 운영 체제(예: 윈도우, 리눅스, 맥OS)와 기기에서 스마트 콘트랙트를 동일한 방식으로 실행하며, 이러한 크로스 플랫폼 호환성을 통해 콘트랙트를 실행하는 모든 노드가 일관된 결과를 얻도록 보장합니다. 대표적인 예로 자바 가상 머신 JVM을 들 수 있습니다.
우리가 일반적으로 블록 브라우저에서 보는 스마트 컨트랙트는 체인에 저장되기 전에 EVM 바이트코드로 컴파일되며, EVM은 컨트랙트를 실행할 때 이러한 바이트코드를 직접 순차적으로 읽으며, 바이트코드에 해당하는 각 명령어(opCode)에는 그에 해당하는 가스 비용이 있습니다.EVM은 실행 중에 각 명령어가 소비하는 가스를 추적하고, 소비되는 양은 작업의 복잡도에 따라 달라집니다. 복잡성에 따라 달라집니다.
또한 이더넷의 핵심 실행 엔진인 EVM은 모든 트랜잭션을 단일 큐에 대기시키고 정해진 순서에 따라 순차적으로 실행하는 등 트랜잭션을 순차적으로 처리합니다. 병렬화를 사용하지 않는 이유는 블록체인은 정합성을 엄격하게 만족해야 하고, 모든 노드에서 동일한 순서로 트랜잭션을 처리해야 하기 때문에 트랜잭션을 병렬로 처리할 경우 그에 맞는 스케줄링 알고리즘을 도입하지 않는 이상 거래 순서를 정확하게 예측하기 어렵고 복잡해지기 때문입니다.
2014-15년 이더리움 창립 팀 시간적 제약으로 인해 연속 실행이 선택되었습니다. 이는 간단하고 유지 관리가 용이하도록 설계되었기 때문입니다. 그러나 블록체인 기술의 반복과 사용자 기반이 점점 더 커지면서 블록체인의 TPS와 처리량에 대한 요구사항이 점점 더 높아졌고, 롤업 기술이 등장하고 성숙해지면서 이더채널의 두 번째 레이어에서 EVM 직렬 실행으로 인한 성능 병목현상이 노출되었습니다.
Layer2의 핵심 구성 요소인 시퀀서는 단일 서버로서 모든 컴퓨팅 작업을 담당합니다. 시퀀서와 함께 작동하는 외부 모듈의 효율이 충분히 높다면 궁극적인 병목 현상은 시퀀서 자체의 효율에 따라 달라질 것입니다. 궁극적인 병목 현상은 시퀀서 자체의 효율성에 따라 달라지며, 이 경우 직렬 실행이 큰 장애물이 될 것입니다.
opBNB 팀은 DA 레이어와 데이터 읽기/쓰기 모듈의 극한 최적화를 통해 시퀀서가 초당 최대 약 2,000회 이상의 ERC-20 전송을 수행하도록 했습니다. 이 수치는 높아 보이지만 처리해야 할 트랜잭션이 ERC-20 전송보다 훨씬 더 복잡하다면 TPS 값은 훨씬 더 낮아질 수밖에 없습니다. 그렇기 때문에 트랜잭션 처리의 병렬화는 앞으로 피할 수 없는 흐름이 될 것입니다.
이제부터 기존 EVM의 한계와 병렬 EVM의 장점을 좀 더 구체적으로 설명해드리겠습니다.
이더리움 트랜잭션 실행의 두 가지 핵심 구성요소
코드 모듈 수준에서, EVM 외에 트랜잭션 실행과 관련된 go-ethereum의 다른 핵심 구성 요소는 Ether 트랜잭션 관리 시스템입니다. 실행과 관련된 go-ethereum의 또 다른 핵심 구성 요소는 stateDB로, 이더의 계정 상태와 데이터 저장을 관리하는 데 사용됩니다. 이더리움은 머클 패트리샤 트리라는 트리 구조를 사용하여 데이터베이스 인덱스(카탈로그) 역할을 하며, EVM에서 트랜잭션을 실행할 때마다 stateDB에 저장된 일부 데이터가 변경되고, 이는 궁극적으로 머클 패트리샤 트리(이후 글로벌 상태 트리라고 함)에 반영됩니다.
특히, stateDB는 다음을 유지 관리합니다. EOA 계정과 컨트랙트 계정을 포함한 모든 이더리움 계정의 상태를 유지하며, 저장된 데이터에는 계정 잔액, 스마트 컨트랙트 코드 등이 포함됩니다. 트랜잭션이 실행되는 동안 stateDB는 해당 계정의 데이터를 읽고 씁니다. 그리고 트랜잭션 실행이 끝나면 stateDB는 지속성을 위해 새로운 상태를 기본 데이터베이스(예: LevelDB)에 제출해야 합니다.
요약하면, EVM은 스마트 컨트랙트 명령을 해석 및 실행하고 계산 결과에 따라 블록체인의 상태를 변경하는 역할을 담당하며, stateDB는 모든 계정과 컨트랙트의 상태 변경을 관리하는 글로벌 상태 저장소 역할을 합니다. 이 두 가지가 협력하여 이더리움의 트랜잭션 실행 환경을 구축합니다.
직렬 실행 세부 사항
이더에는 두 가지 유형의 트랜잭션, 즉 EOA 전송과 컨트랙트 트랜잭션이 있습니다. EOA 이체는 가장 간단한 거래 유형으로, 일반 계정 간 이더를 이체하는 것입니다. 이 유형의 트랜잭션은 컨트랙트 호출을 포함하지 않으며 매우 빠르게 처리됩니다. 단순성으로 인해 EOA 송금은 매우 낮은 가스 수수료를 부과합니다.
단순한 EOA 전송과 달리 컨트랙트 트랜잭션은 스마트 컨트랙트의 호출과 실행을 포함하며, EVM은 컨트랙트 트랜잭션을 처리할 때 스마트 컨트랙트의 바이트코드 명령을 일일이 해석하고 실행해야 하며, 컨트랙트의 로직이 복잡할수록 더 많은 명령과 더 많은 자원이 소모됩니다.
예를 들어 ERC-20 전송의 처리 시간은 EOA 전송보다 약 2배 더 오래 걸리며, 유니스왑의 트랜잭션 작업과 같이 더 복잡한 스마트 컨트랙트의 경우 더 오래 걸리고 심지어 10배 이상 느려질 수 있습니다. 이는 탈중앙 금융 프로토콜이 거래 시점에 유동성 풀링, 가격 계산, 토큰 스왑 등 복잡한 로직을 처리해야 하기 때문에 매우 복잡한 계산이 필요하기 때문입니다.
그렇다면 이 두 구성 요소인 EVM과 stateDB는 어떻게 함께 작동하여 직렬 실행 모드에서 트랜잭션을 처리할까요?
이더의 설계에서 블록 내 트랜잭션은 한 번에 하나씩 순차적으로 처리되며, 각 트랜잭션(tx)은 해당 트랜잭션의 특정 작업을 수행하는 별도의 인스턴스를 갖습니다. 각 트랜잭션은 서로 다른 EVM 인스턴스를 사용하지만, 모든 트랜잭션은 동일한 상태 데이터베이스인 stateDB를 공유합니다.
트랜잭션이 실행되는 동안 EVM은 stateDB와 지속적으로 상호 작용하여 stateDB에서 관련 데이터를 읽고 변경된 데이터를 다시 stateDB에 기록해야 합니다.
트랜잭션이 실행되는 동안 EVM은 stateDB와 지속적으로 상호 작용하여 stateDB에서 관련 데이터를 읽고 변경된 데이터를 다시 stateDB에 기록해야 합니다.
코드 관점에서 EVM과 stateDB가 어떻게 협업하여 트랜잭션을 실행하는지 간략하게 살펴보겠습니다.
1. processBlock() 함수는 Process() 함수를 호출하여 블록에 포함된 트랜잭션을 처리합니다.
2. for 루프는 Process() 함수에 정의되어 있으며 트랜잭션이 하나씩 실행되는 것을 볼 수 있습니다.
3. 모든 트랜잭션이 처리된 후 processBlock() 함수는 writeBlockWithState() 함수를 호출합니다. 를 호출한 다음, 상태 변경 결과를 커밋하기 위해 statedb.Commit() 함수를 호출합니다.
블록의 모든 트랜잭션이 실행되면 블록의 Statedb. 블록의 모든 트랜잭션이 실행되면 stateDB의 데이터는 앞서 언급한 글로벌 상태 트리(Merkle Patricia Trie)에 커밋되고 새로운 stateRoot가 생성됩니다. stateRoot는 블록이 실행된 후 새로운 전역 상태의 "압축 결과"를 기록하는 각 블록의 중요한 파라미터입니다.
EVM의 직렬 실행 모델에서 병목 현상을 쉽게 확인할 수 있습니다. 트랜잭션을 순차적으로 대기열에 올려야 하며, 오래 걸리는 스마트 컨트랙트 트랜잭션이 있으면 다른 트랜잭션은 처리될 때까지 기다릴 수밖에 없으므로 CPU 및 기타 하드웨어 리소스를 최대한 활용하지 못하고 효율성이 제한될 수 밖에 없습니다.
멀티스레드 병렬 최적화를 위한 EVM
실생활의 예를 들어 직렬 실행과 병렬 실행을 비교하면 전자는 카운터가 하나만 있는 은행에 비유할 수 있습니다. 병렬 EVM은 카운터가 여러 개 있는 은행에 비유할 수 있습니다. 병렬 모드에서는 여러 스레드를 열어 여러 트랜잭션을 동시에 처리할 수 있어 효율성이 몇 배 이상 향상될 수 있지만, 까다로운 부분은 상태 충돌 문제입니다.
충돌은 여러 트랜잭션이 동시에 처리될 때 특정 계정의 데이터를 다시 쓰겠다고 선언할 때 발생합니다. 예를 들어 NFT는 1번만 발행할 수 있는데 트랜잭션 1과 2가 모두 해당 NFT를 발행하겠다고 선언하고 두 요청이 모두 충족되면 당연히 오류가 발생합니다. 이러한 상황을 처리하려면 조정된 처리가 필요합니다. 실제로 상태 충돌은 앞서 언급한 것보다 더 자주 발생하는 경향이 있으므로 트랜잭션 처리를 병렬화하려면 상태 충돌에 대처할 수 있는 대책이 필요합니다.
Reddio의 EVM 병렬 최적화 근거
ZKRollup 프로젝트 Reddio에서 EVM의 병렬 최적화 근거를 살펴볼 수 있습니다. . Reddio의 아이디어는 각 스레드에 트랜잭션을 할당하고 각 스레드에 pending-stateDB라는 임시 상태 데이터베이스를 제공하는 것입니다. 자세한 내용은 다음과 같습니다.
1. 트랜잭션의 멀티 스레드 병렬 실행: Reddio는 여러 개의 스레드를 설정하여 서로 다른 트랜잭션을 동시에 처리하고, 스레드들이 서로 간섭하지 않도록 합니다. 따라서 트랜잭션 처리 속도가 몇 배 이상 빨라집니다.
2. 각 스레드에 임시 상태 데이터베이스 할당: Reddio는 각 스레드에 별도의 임시 상태 데이터베이스(pending-stateDB)를 할당합니다. 트랜잭션을 실행할 때 글로벌 stateDB를 직접 수정하는 대신, 각 스레드는 상태 변경 결과를 pending-stateDB에 임시로 기록합니다.
3. 상태 변경 동기화: 블록 내 모든 트랜잭션이 실행된 후 EVM은 각 pending-stateDB에 기록된 상태 변경 결과를 차례로 글로벌 stateDB에 동기화합니다. 다른 트랜잭션이 실행되는 동안 상태 충돌이 발생하지 않으면 보류 중인 상태DB의 기록은 글로벌 상태DB로 원활하게 병합될 수 있습니다.
레디오는 읽기 및 쓰기 작업의 처리 방식을 최적화하여 트랜잭션이 상태 데이터에 올바르게 액세스하고 충돌을 피할 수 있도록 합니다.
-읽기 작업: 트랜잭션이 상태를 읽어야 하는 경우, EVM은 먼저 pending-state의 ReadSet을 확인합니다. ReadSet에 필요한 데이터가 존재한다고 표시되면, EVM은 직접 해당 데이터를 pending-stateDB에서 직접 데이터를 읽습니다. ReadSet에 해당하는 키-값(키-값 쌍)이 없는 경우 이전 블록에 해당하는 글로벌 stateDB에서 과거 상태 데이터를 읽습니다.
-쓰기 작업: strong>모든 쓰기 작업(즉, 상태 수정)은 글로벌 stateDB에 직접 기록되지 않고 먼저 Pending 상태의 WriteSet에 기록됩니다. 트랜잭션이 실행된 후 충돌 감지를 통해 상태 변경 결과를 전역 상태DB에 병합하려고 시도합니다.
병렬 실행의 주요 문제는 다음과 같은 경우 상태 충돌입니다. 여러 트랜잭션이 동일한 계정의 상태를 읽고 쓰려고 시도하는 경우입니다. 이러한 이유로 Reddio는 충돌 감지 메커니즘을 도입했습니다.
- 충돌 감지 : 트랜잭션 실행 중에 EVM은 서로 다른 트랜잭션의 ReadSet과 WriteSet을 모니터링하고, 둘 이상의 트랜잭션이 동일한 상태 항목을 읽거나 쓰려고 시도하면 충돌로 간주합니다. 충돌로 간주합니다.
- 충돌 처리: 충돌이 감지되면 충돌하는 트랜잭션은 재실행을 위해 표시됩니다.
모든 트랜잭션이 실행된 후에는 여러 보류 상태DB의 변경 기록이 전역 상태DB로 병합됩니다. 병합이 성공하면 EVM은 최종 상태를 글로벌 상태 트리에 커밋하고 새 상태 루트를 생성합니다.
멀티스레드 병렬 최적화의 성능 이점은 다음과 같습니다. 특히 복잡한 스마트 컨트랙트 트랜잭션을 처리할 때 분명합니다.
병렬 EVM 연구에 따르면, 충돌이 적은 워크로드(풀에서 충돌이 적거나 동일한 리소스를 사용하는 트랜잭션)의 경우 벤치마크 결과 기존 직렬 실행에 비해 약 3~5배의 TPS 개선 효과가 있는 것으로 나타났습니다. 충돌이 많은 워크로드에서는 모든 최적화를 사용할 경우 이론적으로 60배까지 향상될 수 있습니다.
요약
레디오의 멀티스레드 병렬 최적화는 각 트랜잭션에 임시 상태 저장소를 할당하고 트랜잭션을 다른 스레드에서 병렬로 실행함으로써 EVM의 성능을 크게 향상시킵니다. EVM의 트랜잭션 처리 능력을 크게 향상시킵니다. 읽기/쓰기 작업을 최적화하고 충돌 감지 메커니즘을 도입함으로써 EVM 퍼블릭 체인은 상태 일관성을 보장한다는 전제 하에 트랜잭션의 대규모 병렬화를 달성하여 기존의 직렬 실행 모드에서 발생하는 성능 병목 현상을 해결할 수 있습니다. 이는 향후 이더넷 롤업의 발전을 위한 중요한 토대를 마련합니다.
저장 효율 최적화를 통한 효율성 향상 방법, 높은 충돌에 대한 최적화, GPU 최적화 방법 등 Reddio의 구현에 대한 세부적인 분석을 추가로 진행할 예정입니다.