Deep EVM #2: 메모리 모델 — 스택, 메모리, 스토리지, 콜데이터
Engineering Team
데이터가 존재할 수 있는 네 가지 장소
EVM은 근본적으로 다른 비용, 수명, 접근 패턴을 가진 네 가지 데이터 위치를 제공합니다. 잘못된 것을 선택하는 것이 스마트 컨트랙트의 과도한 가스 소비의 가장 흔한 원인입니다.
| 위치 | 수명 | 읽기 비용 | 쓰기 비용 | 크기 |
|---|---|---|---|---|
| 스택 | 현재 옵코드 | 0 (DUP: 3) | 0 (PUSH: 3) | 1024 워드 |
| 메모리 | 현재 호출 컨텍스트 | MLOAD: 3* | MSTORE: 3* | 확장 가능 |
| 스토리지 | 영구 (블록체인) | SLOAD: 2100/100 | SSTORE: 20000/2900/100 | 2^256 슬롯 |
| 콜데이터 | 현재 호출 컨텍스트 | CALLDATALOAD: 3 | 읽기 전용 | 트랜잭션 입력 |
*메모리 비용은 연산당 3 가스 + 이차 확장 비용.
스택: 빠르지만 작다
스택은 EVM의 작업 메모리입니다. 모든 산술, 비교, 논리 연산은 스택에서 읽고 씁니다. 최대 1024개 요소를 보유하며, 각각 32바이트 폭입니다.
실제로는 DUP과 SWAP 옵코드가 16개 요소 깊이까지만 도달하므로 16개 이상의 스택 슬롯을 사용하는 경우는 드뭅니다. Solidity 컴파일러는 자동으로 스택 레이아웃을 관리하지만, Yul과 Huff에서는 수동으로 관리해야 합니다.
스택 접근은 본질적으로 무료입니다(PUSH/DUP/SWAP 연산에 3 가스). 이것이 스택을 중간 계산, 루프 카운터, 임시 값에 이상적인 위치로 만듭니다.
메모리: 저렴하지만 일시적
메모리는 바이트 주소 지정 가능한 배열로, 비어 있는 상태에서 시작하여 필요에 따라 확장됩니다. 현재 호출 컨텍스트 기간 동안만 유지됩니다.
프리 메모리 포인터
Solidity는 메모리의 처음 128바이트를 특수 목적으로 예약합니다:
| 오프셋 | 목적 |
|---|---|
| 0x00-0x3f | 스크래치 공간 (해싱에 사용) |
| 0x40-0x5f | 프리 메모리 포인터 |
| 0x60-0x7f | 제로 슬롯 |
| 0x80+ | 자유 메모리 (할당 시작) |
메모리 확장 비용
메모리 비용은 MLOAD/MSTORE당 3 가스만이 아닙니다 — 현재 최고 수위 표시를 넘어 메모리에 접근할 때 이차 확장 비용이 부과됩니다.
공식 (옐로 페이퍼에서):
memory_cost = (memory_size_words^2 / 512) + (3 * memory_size_words)
| 사용된 메모리 | 비용 |
|---|---|
| 32 바이트 (1 워드) | 3 가스 |
| 1 KB (32 워드) | 98 가스 |
| 10 KB (320 워드) | 1160 가스 |
| 100 KB (3200 워드) | 29600 가스 |
| 1 MB (32000 워드) | 2,001,000 가스 |
스토리지: 영구적이지만 비용이 높다
스토리지는 EVM의 영구 키-값 저장소입니다. 각 컨트랙트에는 2^256개의 32바이트 슬롯을 가진 격리된 스토리지 공간이 있습니다.
스토리지 패킹
Solidity는 가능할 때 여러 작은 변수를 단일 32바이트 슬롯에 패킹합니다:
// 나쁨: 3 스토리지 슬롯 = 3 x SLOAD = 6300 가스 (cold)
contract Unpacked {
uint256 a; // 슬롯 0
uint256 b; // 슬롯 1
uint256 c; // 슬롯 2
}
// 좋음: 1 스토리지 슬롯 = 1 x SLOAD = 2100 가스 (cold)
contract Packed {
uint64 a; // 슬롯 0, 바이트 0-7
uint64 b; // 슬롯 0, 바이트 8-15
uint64 c; // 슬롯 0, 바이트 16-23
}
콜데이터: 읽기 전용이고 저렴
콜데이터는 트랜잭션이나 호출과 함께 전송되는 입력 데이터입니다. 읽기 전용이며 접근 비용이 저렴합니다(CALLDATALOAD에 3 가스).
일시적 스토리지 (EIP-1153)
EIP-1153(칸쿤 업그레이드, 2024년 3월)은 TSTORE와 TLOAD를 도입했습니다. 일반 스토리지와 유사하지만 트랜잭션 종료 시 자동으로 지워집니다.
- TLOAD와 TSTORE 모두 100 가스 비용
- 재진입 잠금에 이상적 — SSTORE의 20000 가스 비용 없이
- 트랜잭션 내 교차 컨트랙트 통신에 적합
실용적 최적화: 올바른 위치 선택
- 스택에 넣을 수 있나? 스택을 사용. 한계 비용 제로.
- 16개 이상의 값이 필요한가? 메모리 사용.
- 읽기 전용 입력 데이터인가? 콜데이터 사용. 32바이트 읽기당 3 가스.
- 트랜잭션 간에 지속되어야 하는가? 스토리지 사용. 새 항목에 20000+ 가스 예산.
- 호출 프레임 간에 지속되지만 트랜잭션 간에는 안 되는가? 일시적 스토리지 사용. 100 가스.
결론
EVM의 메모리 모델은 겉보기에 단순합니다 — 명확한 트레이드오프를 가진 네 가지 위치. 그러나 비용 차이는 엄청납니다: 스택 ADD는 3 가스인 반면 SSTORE는 20000 가스입니다. 이 비용을 직감적 수준에서 이해하는 것이 가스 효율적 컨트랙트와 가스 낭비 컨트랙트를 구분짓습니다.