Wasm 런타임, 성능을 위한 세 가지 선택!

by DD
1개월 전
조회수 4

WebAssembly(Wasm)은 가볍고, 실행하기 쉬운 바이트코드 형식으로, 런타임 구현 학습에 적합하다는 평가를 받음.

저자는 세 가지 Wasm 실행 방식(In-Place Interpreter, Unrolled Interpreter JIT, Single-Pass JIT)을 구현하고 성능을 비교 분석함.

In-Place Interpreter는 벤 티처(Ben Titzer)의 논문을 기반으로 하며, 제어 흐름 최적화에 중점을 둠.

Single-Pass JIT는 레지스터 할당, 호출 규약, 목적지 레지스터 패칭(Destination Register Patching) 등 다양한 최적화 기법을 활용하여 성능을 향상시킴.

Wasm 런타임의 핵심: 검증(Validation) 단계

저자는 Wasm 런타임의 핵심 요소 중 하나로 검증(Validation) 단계를 강조하며, 이 단계에서 추상적 해석(Abstract Interpretation)을 통해 함수 본문을 검증한다고 설명한다. 특히, 테일콜 인터프리터(Tail-call Interpreter) 스타일을 활용하여 검증 과정을 효율적으로 구현했다. 이 방식은 각 명령어에 대한 맞춤형 검증 프로세스를 가능하게 하며, 인터프리터의 속도를 향상시키는 데 기여한다. 또한, 스택 다형성(Stack Polymorphism)과 같은 Wasm의 특성을 고려하여 검증 로직을 설계해야 함을 언급한다.

In-Place Interpreter의 제어 흐름 최적화

In-Place Interpreter는 벤 티처(Ben Titzer)의 연구를 기반으로 하며, Wasm의 제어 흐름을 효율적으로 처리하기 위해 사이드 테이블(Side Table)을 활용한다. 저자는 점프(Jump) 명령어를 처리하는 과정에서 0, 1, 2+개의 반환 값을 처리해야 하는 어려움을 겪었으며, 2+ 케이스를 매우 드문 경우로 처리하여 성능을 최적화했다. 특히, `slow_memmove` 함수를 사용하여 2개 이상의 반환 값을 처리하는 경우를 매우 드문 경로로 분리하여 성능 저하를 최소화했다.

Unrolled Interpreter JIT의 성능 향상 기법

Unrolled Interpreter JIT는 인터프리터의 주요 병목 지점인 명령어 fetch 및 실행 로직을 최적화하기 위해 고안되었다. 저자는 LEB128 인코딩(LEB128 Encoding)으로 인한 성능 저하를 해결하기 위해, 명령어에 대한 정보를 코드 자체에 임베딩하는 방식을 사용했다. 또한, `dummy` 함수와 `[[clang::musttail]]` 속성을 활용하여 컴파일러가 레지스터를 최적화하도록 유도하고, 제어 흐름 명령어를 효율적으로 처리했다.

Single-Pass JIT의 레지스터 할당 및 최적화

Single-Pass JIT는 레지스터 할당(Register Allocation)을 통해 명령어 간의 값 전달을 최적화하고, 호출 규약(Calling Convention)을 커스터마이징하여 성능을 향상시켰다. 특히, 목적지 레지스터 패칭(Destination Register Patching) 기법을 통해 `local.set` 명령어를 최적화하여 불필요한 `mov` 명령어를 제거했다. 이러한 최적화는 ARM 아키텍처의 특성을 활용하여 구현되었으며, 코드 생성 속도와 실행 속도 사이의 균형을 맞추는 데 기여했다.

성능 비교 및 분석

저자는 In-Place Interpreter, Unrolled JIT, Single-Pass JIT의 성능을 다양한 벤치마크를 통해 비교 분석했다. 특히, Figma의 Wasm 바이너리를 사용하여 시작 속도를 측정하고, esbuild, Spidermonkey, Coremark 벤치마크를 통해 실행 속도를 비교했다. 그 결과, Single-Pass JIT가 Node.js보다 esbuild 실행 속도에서 우위를 보였으며, Wasmtime과 유사한 성능을 보여주었다. 하지만, 저자는 자신의 JIT가 Wasmtime과 Node.js에 비해 안전성 측면에서 부족할 수 있음을 언급하며, 런타임 구현의 다양한 측면을 고려해야 함을 강조했다.

A Tale of Three WebAssembly Runtimes