다운로드
닫기 메뉴 -

Rif 컨센서스 노드의 월드 스테이트 부하 검사

Published on: 22 6월, 2020

작성자: Pablo Pedemonte

소개

RSK는 2016년에 이더리움의 월드 스테이트를 모델로 하는 머클 패트리샤 트리의 대안으로 유니트리(Unitrie)를 제안하였습니다(이더리움 옐로우 페이퍼, 부록 D 참고). 또 2020년 1월부로는 RSK의 Rif 컨센서스 노드(Besu 코드 기반)의 유니트리를 선택 기능으로 추가하기도 했습니다. 따라서 사용자는 이제 다음 두 가지의 월드 스테이트 구현 중 하나를 고를 수 있습니다.

  1. 옐로우 페이퍼에 설명된 것과 같은 Besu 월드 스테이트(또는 클래식 월드 스테이트).
  2. 유니트리가 지원하는 유니트리 월드 스테이트.

이렇게 두 가지의 서로 다른 월드 스테이트 구현 중 하나를 골라 Rif 컨센서스 노드를 실행할 수 있는 기회를 통해서는 각 월드 스테이트 변형의 이점을 비교해 볼 수 있습니다. 이 기사에서는 두 가지의 구현 모두를 비교하고, 구축/검색 시간과 제공되는 저장 공간을 모두 소모하기 전의 계정 할당 수용력을 분석해 보도록 하겠습니다.

1번 실험: 구축 및 검색 시간

구축 및 검색 시간 분석을 위해서는 초기에 아무 것도 없는 상태의 이더리움 월드 스테이트에 특정 수의 계정을 추가하는 것과, 벤치마크 실행 중에 변화(즉 월드 스테이트 트리/유니트리에 생성된 계정 저장하기)를 50번 커밋하는 것으로 구성된 벤치마크를 수행하였습니다. 일단 계정을 커밋하고 나면 다음 두 가지 변형으로 백만 개의 계정을 검색하였습니다. 

  1. 백만 개의 기존 계정 검색 (모든 계정이 발견됨).
  2. 백만 개의 랜덤 계정 검색 (실제로 발견된 계정 없음).

이 벤치마크는 최종적으로 구축 및 검색 시간을 보고합니다. 이때 벤치마크는 3백만 개에서 천만 개에 걸친 외부 소유 계정을 이용해 4Gb Java heap에서 수행되었습니다. 각 계정 수에 따라 벤치마크를 여섯 번 실행하여 변화를 수용하였습니다. 다음 표는 3백만, 6백만, 그리고 9백만 개의 계정에 대한 평균 구축 및 검색 시간을 보여줍니다. CT는 Construction Time(구축 시간), ELT는 Existing Accounts Lookup Time(기존 계정 검색 시간), 그리고 RLT는 Random Accounts Lookup Time(랜덤 계정 검색 시간)을 뜻합니다. 모든 시간은 초로 표시되었습니다.

표 1. 구축 및 검색 시간

저희의 유니트리 구현은 메모리 효율적으로 고안되었으며 대부분의 경우에 기꺼이 수행 시간을 늘리고 메모리 사용량을 줄입니다. 이 선택 때문에 유니트리는 검색 시간에서 이더리움의 트리에 불리하며, 유니트리가 이더리움의 트리보다 1.2에서 1.8배 느립니다. 그러나 이는 할당된 계정의 수를 고려할 때 가치가 있습니다. 유니트리 메모리는 9백만 개의 계정을 처리할 수 있는 반면, Besu의 월드 스테이트 구현은 6백만 개를 겨우 달성합니다. 실제로 6백만 개의 계정으로 트리를 구축하는 것은 클래식 트리의 경우보다 1.47배 느립니다. 이는 Besu가 6백만 개의 계정 한계치를 수행하기 위해 JVM을 스레싱하고 있음을 보여줍니다.

 

표 2. 구축 및 검색 처리량

표 2는 구축 및 검색 처리량의 개요를 나타냅니다. CTp라는 두음문자는 Construction Throughput(구축 처리량)을 줄인 것으로, 초당 유니트리/이더리움에 제공되는 업데이트의 수를 계산합니다. 그와 비슷하게 ELTp RLTp는 각각 초당 Existing Account Lookup(기존 계정 검색 처리량)과 Random Account Lookup Throughput(랜덤 계정 검색 처리량)을 뜻합니다. 유니트리는 일반적으로 Besu의 월드 스테이트가 특정 시간 안에 실행할 수 있는 작업의 55% – 80% 사이를 실행할 수 있습니다. 알려진 바에 따르면 스레싱 때문에, 유니트리의 처리량은 6백만 개의 계정을 추가할 때 Besu의 월드 스테이트의 1.5배가 됩니다. 또 이와 같은 계정 수에서 유니트리의 백만 개의 랜덤 요소에 대한 검색 처리량은 Besu의 월드 스테이트와 일치했습니다. 

이때 최악의 경우 검색 처리량은 초당 67,000개 검색임을 참고하시기 바랍니다(9백만 개의 계정, 기존 계정 검색). 이런 수치가 납득할 만한 수치임을 확실하게 하고자 400 가스가 소모되는 BALANCE 작업을 고려해 보도록 하겠습니다. RSK 블록의 현 가스 제한이 6백 80만 가스(6.8M)인 것을 고려했을 때, 이상적으로 블록당 6.8M/400 = 17K BALANCE의 작업을 수행할 수 있으며, 이는 초당 67개를 검색한다고 해도 트리가 메모리에 저장되어 있는 한 유니트리가 0.25초만에 수행할 수 있는 작업입니다.

그림 1, 2, 그리고 3은 각각 [3백만, 천만] 범위의 계정을 할당할 때의 구축 및 기존/랜덤 계정 검색 시간을 나타냅니다. 이때 구성은 각 데이터 포인트의 중심 집중 경향과 신뢰 구간을 표시합니다. 4Gb Java heap의 경우 유니트리가 9백만 계정의 한계점에서 스레싱을 시작함을 참고하시기 바랍니다(반면 Besu의 월드 스테이트는 6백만 한계점에서 스레싱). 또 유니트리는 신뢰 구간이 더 넓은 것을 볼 수 있습니다. 이는 유니트리 구현이 공유 경로를 소프트 레퍼런스로 저장해 실행 시간에 비결정적으로 경로 재계산 시간을 추가하기 때문입니다.

2번 실험: 컨트랙트 계정

유니트리는 각 계정의 주소 해시에서 파생된 키 아래에 해당 계정을 저장합니다. 구조상 계정 키는 랜덤한 고정 영역과 동일한 길이를 유지하므로, 결과는 평균적으로 계정이 잎에 포함되는 이진법 균형 트리가 됩니다. 이는 N개의 외부 소유 계정의 경우 2N – 1개의 노드가 필요하다는 뜻입니다.

컨트랙트 계정을 고려할 때, 유니트리의 저장 용량은 기존 계산보다 줄어듭니다. 이는 유니트리가 해당 계정 노드의 올바른 자식으로 코드를 저장하기 때문입니다. 따라서 N개 계정에서 α가 컨트랙트 간의 비율이라면 다음과 같이 가정할 수 있습니다.

그림 1. 월드 스테이트 구축 시간

그림 2. 검색 시간 (기존 계정)

그림 3. 검색 시간 (랜덤 계정)

외부 소유 계정 간의 비율이라면 유니트리 크기는 (2 + α)N – 1 노드가 됩니다. 이때 범위는 α=0일때는 2N – 1 노드(따라서 N개의 외부 소유 계정의 초기 계산을 포함함)에서 α=1일때는 3N – 1 노드까지(모든 계정이 컨트랙트임) 다양합니다. 반면 이더리움의 월드 스테이트는 트리 밖에 계정 코드를 저장하므로, 컨트랙트 계정을 고려할 때 트리의 저장 용량이 영향을 받지 않습니다.

α의 여러 값에 따른 유니트리의 저장 용량 변형을 계산하기 위해 스레싱 전에 4Gb Java heap에서 최대한 많은 계정을 할당하는 벤치마크를 실행해 보았습니다. 이때 [0,1] 간격으로 α의 여러 값에 대한 실험을 진행했습니다. 해당 벤치마크는 계정 코드에 실세계의 2114 바이트 ERC20 컨트랙트를 사용하였습니다. 그림 4는 그 결과로, 유니트리가 Besu 이더리움 월드 스테이트의 최대 저장 용량을 넘어서는 포인트를 α=0.8로 정확하게 표시하고 있습니다. 이는 또 4Gb heap의 유니트리 작업 범위를 표시하고 있기도 합니다. 이는 9백 50만/9백만 외부 소유 계정(α=0)에서 4백 90만 컨트랙트 계정(α=1)을 저장할 수 있습니다.

그림 4. 컨트랙트 계정에 따른 저장 용량 변형

결론

유니트리는 처리 시간을 희생해 메모리 수용량을 줄입니다. 2번과 3번 그림을 보면 [3백만, 5백만] 범위의 계정의 경우 유니트리 검색 시간이 1.5배에서 2배 느린 것을 알 수 있습니다. 그러나 Besu의 월드 스테이트가 메모리에 충분한 월드 스테이트를 저장할 수 없어 디스크에서 노드를 읽게 되는 상황에서 해당 차이는 소멸하게 됩니다. 이런 경우 디스트 I/O 지연은 검색 시간을 지배하게 됩니다. 저희는 실험 결과 유니트리는 4Gb heap에 9백만 개의 외부 소유 계정으로 구성된 월드 스테이트를 저장할 수 있는 반면, Besu의 월드 스테이트는 6백만 계정 한계치에서 점차 저하되는 것을 확인하였습니다. 전반적으로 유니트리는 디스크 I/O를 피할 수 있는 가능성이 더 높은 대신, 검색 시간이 Besu의 월드 스테이트보다 1.8배 (최악의 경우) 느립니다.

컨트랙트 계정을 고려할 때 저장 용량은 컨트랙트와 외부 소유 계정 간의 비율인 α로 결정됩니다. α의 값이 낮을수록 메모리에 저장할 수 있는 노드는 더 많아지고, 이는 다시 디스크 I/O 지연을 피할 수 있는 가능성을 최대화하게 됩니다. 실험에 따르면 α < 0.8의 경우 유니트리의 저장 용량은 Besu의 것보다 높은 것이 확인되었습니다.

이때 α의 예상값은 얼마일까요? Etherscan의 보고에 따르면 2020년 11월 기준 메인넷에는 8천 28만 개의 고유 계정이 존재합니다. 같은 날짜의 컨트랙트 계정 수는 1천 2백 40만개였습니다. 따라서 α=0.154가 됩니다. 이 글을 작성하는 시기에 메인넷에는 9천 4백 14만개의 고유 주소가 존재합니다. 2020년 11월 이후로 생성된 모든 계정이 컨트랙트라는 비현실적인 가정을 해 본다면, α=0.28이라는 아주 과장된 상한선이 주어집니다. 이렇게 과장된 가정을 한다고 해도 이는 여전히 유니트리가 해결할 수 있는 범위 안에 존재합니다.

부록 A: 실험 실행 조건

모든 실험 코드는 Github 프로젝트 rif-consensus-node에 호스팅되었으며, 여기에는 JDK 11 이상이 필요합니다. 프로젝트를 복제하고 난 후 프로젝트의 루트 폴더로 이동해 다음을 수행하면 유니트리 부하 검사나 이더리움 클래식 월드 스테이트 검사를 실행할 수 있습니다.

accounts_to_create가 생성할 계정 수를 정의하고, alpha_ratio 한도가 2번 실험에서 정의한 컨트랙트와 외부 소유 계정 간의 비율에 해당함.

실험은 인텔 i5 2.3 GHz 듀얼 코어 프로세서와 RAM 16Gb, SSD 250GB를 갖춘 맥북 프로로 실행하였습니다. 모든 실험은 G1 garbage collector를 이용해 4Gb 최대 사이즈 JVM heap에서 수행하였습니다.