추상적인
이더리움은 블록 가스 한도를 점진적으로 높여 L1 스토리지를 확장하고 있습니다. 그러나 가스 한도를 크게 높이면(예: Dankrad가 제안한 100배 증가 ) 디스크 I/O 및 CPU 실행 속도와 같은 하드웨어 한계에 빠르게 도달합니다. 사전 워밍업과 이더리움 개선 제안(EIP)-7928의 블록 레벨 액세스 리스트(밸런서(BAL))는 대부분의 I/O 읽기 지연을 제거하여 주요 병목 현상을 실행 자체로 옮깁니다. 한편, 현재 클라이언트들은 여전히 트랜잭션을 순차적으로 실행하기 때문에 근본적으로 처리량에 제약이 있습니다.
밸런서(BAL) ( 저희 팀이 2년 전에도 탐구했던 아이디어 )은 완벽한 병렬 실행을 가능하게 하지만, 성능 한계는 아직 불분명합니다. 이 질문에 답하기 위해 다음과 같은 구성으로 순수 실행 환경을 구축했습니다.
- 사전 로드된 상태는 관련 계정, 저장 슬롯 및 계약 코드가 밸런서(BAL) 힌트를 통해 미리 해결되는 환경을 시뮬레이션합니다.
- 대부분의 클라이언트에 이미 구현된 병렬 송신자 복구 기능을 활용하는 사전 복구된 tx 송신자;
- 상태 루트 계산을 생략하면 블록 크기가 커질수록 해당 비용을 분산할 수 있습니다.
이 환경을 사용하여 밸런서(BAL) 통해 트랜잭션별 병렬 실행 벤치마킹을 수행했습니다. 그 결과, 최신 16코어 범용 PC에서 순수 실행 처리량이 10기가가스/초를 초과하는 것을 확인했습니다. 반면, 현재 Reth 클라이언트는 동일 조건에서 약 1.2기가가스/초의 처리량만 달성합니다. 이는 앞서 언급한 병목 현상이 완전히 해결되면 이더리움 가상 머신(EVM) 실행이 현재 클라이언트 기준선보다 한 자릿수 이상 확장될 수 있음을 시사합니다.
오늘날 우리가 있는 곳
이더리움은 후사카 업그레이드에서 가스 한도를 45M에서 60M으로 늘립니다. 가스 한도가 100배 증가한다고 가정하면, 생성되는 블록 약 4.5G의 가스가 포함될 것입니다. 따라서 검증 시간을 3초 미만으로 유지하려면 검증자는 최소 1.5기가가스/초의 처리량이 필요합니다. 그러나 Base의 공개 벤치마크에 따르면 일반적인 하드웨어에서 최신 클라이언트가 도달할 수 있는 최대 처리량은 약 600MGas/초에 불과합니다. 이러한 제한은 주로 순차 실행 때문입니다. 멀티코어 CPU를 사용할 수 있음에도 불구하고 기존 클라이언트는 트랜잭션을 순차적으로 처리하여 대부분의 코어가 제대로 활용되지 못하고 있습니다.
| Tx 페이로드 | 게스 MGas/s | 레스 MGas/s |
|---|---|---|
| 기본 메인넷 시뮬레이션 | 316.4 | 591.6 |
현재 성능(~0.6 GGas/s)과 100배 확장에 필요한 성능(~1.5 GGas/s) 사이의 격차는 여전히 상당하며, 이는 우리가 완전 병렬 이더리움 가상 머신(EVM) 실행을 추진하는 이유입니다.
우리는 어떻게 했을까?
밸런서(BAL) 제공하는 궁극적인 병렬 실행 성능을 연구하기 위해, 실행과 관련 없는 모든 부분을 제거하여 순수 실행 환경을 구축함으로써 BAL 기반 병렬 처리의 진정한 상한선을 측정할 수 있었습니다. Rust의 가비지 컬렉션(GC)이 없는 설계, 멀티스레드 스케줄링에 대한 세밀한 제어, 그리고 Reth의 높은 성능을 활용하여 Reth 클라이언트를 수정하고, 이 실험에서는 revm을 이더리움 가상 머신(EVM) 실행 엔진으로 사용했습니다.
순수 실행 에뮬레이션을 위한 단순화
- 전체 체인 상태는 미리 메모리에 로드됩니다(BAL의 읽기 위치를 고려하면 I/O를 일괄 처리할 수 있기 때문입니다).
- 모든 거래는 발신자 정보가 이미 복구된 상태로 진행됩니다(발신자 정보 복구는 사전에 완벽하게 병렬화될 수 있습니다).
- 실행 후 상태 루트 계산 및 데이터베이스 커밋은 수행되지 않습니다(이는 병목 현상이지만 본 연구의 주요 초점은 아닙니다).
엔지니어링 작업 및 설치
- Reth 클라이언트를 수정하여 블록, BAL, 최근 256 블록 해시, 밸런서(BAL) 읽기 세트 힌트에서 확인된 사전 블록 상태를 포함한 전체 실행 종속성을 덤프할 수 있도록 했습니다.
- Revm이
blockEnv,state,txEnv를 로드하고 트랜잭션별로 별도의 이더리움 가상 머신(EVM) 인스턴스를 생성할 수 있도록 어댑터를 추가했습니다. - 병렬 처리 세분성 = 트랜잭션별 .
- 하드웨어: AMD 라이젠 9 5950X (16코어), 128GB RAM.
- 데이터 세트: 메인넷 블록 2000개(
#23600500–23602500). - 측정 기준: 초당 가스 소모량 = 총 가스 소모량 / 순수 실행 에뮬레이션 시간.
벤치마크 제품군은 여기에서 확인할 수 있습니다.
https://github.com/dajuguan/evm-benchmark
결과
저희는 먼저 revm의 순차 성능을 최적화한 후, 점진적으로 병렬 실행을 도입하는 방식으로 평가를 진행했습니다. 병렬 확장성 분석 결과, 가장 오래 걸리는 트랜잭션의 지연 시간이 전체 속도 향상을 제한하는 핵심 요소임을 확인했습니다. 이러한 제약을 완화하기 위해 더 큰 블록 가스 제한을 시뮬레이션했고, 이를 통해 밸런서(BAL) 에서 상당한 병렬 처리가 가능해졌습니다. 16개의 스레드와 1G의 블록 가스 제한을 사용했을 때, 순수 실행 처리량은 약 14 GGas/s 에 달했습니다.
순차 실행을 통한 기준선 정렬
저희는 먼저 Reth의 벤치마크 결과를 재현하려고 시도했습니다. KZG 설정이 미리 로드된 상태에서 메인넷 데이터를 순차적으로 실행한 결과, 순수 실행 속도는 1,212 MGas/s에 도달했습니다.
이 순차적인 결과는 이후 모든 실험의 기준점이 됩니다.
병렬 실행과 그 핵심 경로 병목 현상
실제 속도 향상과 트랜잭션 수준 병렬 처리에 대한 암달의 법칙의 영향을 평가하기 위해, 우리는 트랜잭션별 병렬 실행 실험을 수행하여 가장 오래 실행되는 트랜잭션이 달성 가능한 속도 향상에 미치는 영향을 정량화했습니다.
자세한 결과는 아래와 같습니다(여기서 "가장 긴 트랜잭션 지연 시간"은 각 블록 에서 가장 오래 실행된 트랜잭션의 총 실행 시간입니다).
| 실 | 처리량(MGas/s) | 가장 긴 TX 지연 시간 | 총 소요 시간 |
|---|---|---|---|
| 1 | 1258 | 6.06초 | 33.47초 |
| 2 | 2460 | 6.04초 | 17.12초 |
| 4 | 3753 | 6.10초 | 10.71초 |
| 8 | 4824 | 6.00초 | 8.73초 |
| 16 | 5084 | 6.04초 | 8.29초 |
전반적으로 확장성 결과는 암달의 법칙과 매우 유사합니다. 스레드 수가 증가함에 따라 처리량은 증가하지만, 블록 실행 시간은 가장 긴 트랜잭션에 의해 제한됩니다. 16개 스레드 환경에서는 이 트랜잭션이 전체 실행 시간의 약 70%를 차지하므로, 16코어 시스템에서 이상적인 16배 속도 향상에는 미치지 못하고 약 5배로 제한됩니다. 이는 확장성이 단순히 컴퓨팅 용량보다는 블록별 중요 경로에 의해 결정됨을 시사합니다.
이러한 핵심 경로 제한은 가장 긴 트랜잭션의 지배력을 줄임으로써 완화할 수 있으며, 예를 들어 이더리움 개선 제안(EIP)-7825: 트랜잭션 가스 한도 상한 설정 이나 블록 가스 한도 증가(본 논문에서 살펴보는 접근 방식)를 통해 해결할 수 있습니다.
7928 + 메가 블록 = 엄청난 병렬 처리
블록별 중요 경로가 동시성을 제한하기 때문에 병렬 처리를 높이기 위해 더 많은 가스를 소모하는 "메가 블록"을 사용하는 실험을 진행했습니다. 이를 시뮬레이션하기 위해, 메인넷의 여러 연속 블록(메가 블록 또는 배치)의 트랜잭션을 병렬로 실행한 후, 배치에 포함된 모든 트랜잭션이 완료된 후에만 상태를 커밋(실험에서는 아무 작업도 수행하지 않음)했습니다. 이렇게 하면 여러 블록이 하나의 큰 실행 단위로 통합됩니다.
메가블록 워크로드에서의 병렬 처리 분석
먼저, 다양한 스레드 수를 사용하여 평균 블록 가스 사용량 1,053M을 시뮬레이션하는 50개의 블록 배치에 대해 평가를 진행했습니다. 전체 결과는 아래에 나와 있습니다.
| 실 | 처리량(MGas/s) | 가장 긴 TX 지연 시간 | 총 소요 시간 |
|---|---|---|---|
| 1 | 1,440 | 0.50초 | 29.26초 |
| 2 | 2,793 | 0.50초 | 15.08초 |
| 4 | 5,167 | 0.52초 | 8.15초 |
| 8 | 9,095 | 0.54초 | 4.63초 |
| 16 | 14,001 | 0.59초 | 3.01초 |
이렇게 큰 블록 크기를 사용하면 가장 오래 걸리는 트랜잭션이 더 이상 핵심 경로를 지배하지 않습니다. 16개 스레드 환경에서는 전체 실행 시간의 20% 미만을 차지합니다. 처리량은 스레드 수에 거의 선형적으로 비례하여 증가합니다. 16개 스레드를 사용하면 14 GGas/s의 처리량을 달성할 수 있는데, 이는 순차 실행 대비 약 10배의 속도 향상이며 이상적인 선형 확장에 가깝습니다. 이는 매우 고무적인 결과입니다. 실험에서 남은 주요 핵심 경로는 point_evaluation 사전 컴파일인데, 이는 병렬화가 쉽지 않습니다.
블록 가스 사용량 변화에 따른 처리량
블록 가스 사용량 증가에 따른 병렬 실행의 확장성을 평가하기 위해, 단일 메가 블록 으로 묶이는 블록 수인 블록 배치 크기를 변경하면서 연속적인 블록 배치를 실행하여 다양한 실제 블록 가스 사용량을 시뮬레이션했습니다.
| 실 | 블록 배치 크기 | 평균 블록 가스(M) | 처리량(MGas/s) |
|---|---|---|---|
| 16 | 1 | 21 | 5,084 |
| 16 | 2 | 42 | 6,641 |
| 16 | 5 | 105 | 8,814 |
| 16 | 10 | 210 | 10,228 |
| 16 | 25 | 526 | 12,152 |
| 16 | 50 | 1,053 | 14,001 |
| 16 | 100 | 2,106 | 14,887 |
| 16 | 200 | 4,212 | 15,298 |
블록 가스 사용량이 증가함에 따라 처리량은 계속 증가하지만, 병렬 처리 성능 향상은 블록 가스 사용량이 두 배로 증가할 때마다 약 30%에서 약 3%로 감소합니다. 배치 크기가 약 50개 블록(약 1,053M 블록 가스)을 초과하면 블록 가스 사용량을 더 늘려도 처리량 증가는 미미해집니다.
시야
저희 실험 결과에 따르면 이더리움 개선 제안(EIP)-7928을 메가 블록과 결합하면 트랜잭션 실행이 매우 효율적으로 확장되어 최신 16코어 범용 프로세서에서 순수 실행 처리량 14기가가스/초를 달성할 수 있습니다. 하지만 여전히 몇 가지 해결해야 할 문제가 남아 있습니다.
1. 발신자 복구
순수 실행 벤치마크에서는 송신자 복구 기능을 제외했습니다. 실험 결과, 이 기능을 활성화하면 처리량이 약 2/3 감소하여 메가 블록 구성(1,053M 블록 가스)에서 약 5기가가스/초까지 떨어지는 것으로 나타났습니다.
가능한 완화책: GPU 가속 송신자 복구.
2. 가스 가격 책정 모델
7702 트랜잭션에 대한 point_evaluation 사전 컴파일 및 송신자 복구는 시간당 가스 효율성이 낮습니다. 이더리움 개선 제안(EIP)-7928 시대에는 해당 가스 가격 책정 방식을 재검토해야 할 수도 있습니다.
3. 거래 가스 한도
블록 가스 한도를 높이려면 높은 병렬성을 유지하기 위해 현재 트랜잭션 가스 한도를 유지해야 할 수도 있습니다.
4. 밸런서(BAL) 건설 가속화
빌더 성능이 주요 병목 현상이 될 것으로 예상됩니다. 순수 실행 처리량을 유지하기 위해서는 밸런서(BAL) 구축 성능을 개선하는 것이 필수적입니다.
5. 상태 커밋 최적화
상태 커밋 또한 주요 병목 현상 중 하나입니다. 높은 처리량 실행을 유지하려면 상태 루트 계산 속도를 높이고 트라이 커밋을 최적화해야 합니다.
기타 작품
또한, 가스 사용량이나 가스 제한량을 기준으로 정렬하여 가스 소모량이 많은 트랜잭션의 우선순위를 지정하는 방식과, 트랜잭션이 블록 순서대로 유지되고 각 신규 트랜잭션이 사용 가능한 첫 번째 코어에 할당되는 단순 순서 목록 스케줄러(OLS)를 포함한 다양한 작업 스케줄링 전략을 살펴보았습니다. 그러나 메인넷 데이터에 적용했을 때, 가스 소모량이 많은 트랜잭션의 우선순위 지정은 성능 향상에 미미한 효과만 보였으며 전체 처리량에는 큰 영향을 미치지 않았습니다.
다양한 스케줄링 전략에 따른 처리량
전체 처리량에 미치는 영향을 평가하기 위해, 가스 사용량 또는 가스 한도에 따라 중질 가스 거래를 먼저 예약하는 방식과 OLS(최종 예약)를 비교했습니다.
- 일반 블록에 대한 결과:
| 스레드(스케줄러) | 처리량(MGas/s) | 가장 긴 Txs 지연 시간 | 총 소요 시간 |
|---|---|---|---|
| 2 (사용된 가스) | 2,726 | 5.70초 | 15.45초 |
| 2 (가스 제한) | 2,728 | 5.68초 | 15.44초 |
| 2 (OLS) | 2,460 | 6.04초 | 17.12초 |
| 4 (사용된 가스) | 4,401 | 6.09초 | 9.57초 |
| 4 (가스 제한) | 4,321 | 6.18초 | 9.75초 |
| 4 (OLS) | 3,753 | 6.10초 | 10.71초 |
| 8 (사용된 가스) | 5,455 | 6.15초 | 7.72초 |
| 8 (가스 제한) | 5,426 | 6.13초 | 7.76초 |
| 8 (OLS) | 4,824 | 6.00초 | 8.73초 |
| 16 (사용된 가스) | 5,643 | 6.03초 | 7.47초 |
| 16 (가스 제한) | 5,531 | 6.05초 | 7.62초 |
| 16 (OLS) | 5,084 | 6.04초 | 8.28초 |
- 평균 블록 가스 1053M의 메가 블록에 대한 결과:
| 스레드(스케줄러) | 처리량(MGas/s) | 가장 긴 Txs 지연 시간 | 총 소요 시간 |
|---|---|---|---|
| 2 (가스 제한) | 2,732 | 0.53초 | 15.42초 |
| 2 (OLS) | 2,793 | 0.50초 | 15.08초 |
| 4 (가스 제한) | 5,114 | 0.54초 | 8.24초 |
| 4 (OLS) | 5,167 | 0.52초 | 8.15초 |
| 8 (가스 제한) | 9,082 | 0.57초 | 4.64초 |
| 8 (OLS) | 9,095 | 0.54초 | 4.63초 |
| 16 (가스 제한) | 14,181 | 0.63초 | 2.97초 |
| 16 (OLS) | 14,001 | 0.59초 | 3.01초 |
토니의 분석에 따르면, 최악의 시나리오에서 가스량이 많은 트랜잭션에 우선순위를 부여하는 방식이 OLS보다 20~80% 더 나은 성능을 보일 수 있다고 합니다. 하지만 실제 메인넷 데이터(평균적인 경우를 나타냄)를 사용해 보면 개선 효과는 약 10%에 불과하며, 가스 제한, 가스 사용량, OLS를 기준으로 스케줄링하더라도 성능 차이는 미미합니다. 메가 블록에서는 OLS가 가스 제한 스케줄링과 거의 동일한 성능을 보입니다. 이러한 관찰 결과는 트랜잭션 스케줄링 자체가 주요 병목 현상이 아니라, 메인넷에서 트랜잭션이 분산되는 방식이 핵심적인 병목 현상을 형성한다는 것을 시사합니다.


