TECH 으로 돌아가기
TECH HACKER NEWS 어제 8분 읽기 43 READS

Wasmtime 47, GC와 예외 기본 탑재… 더 많은 언어가 WebAssembly로

WebAssembly 런타임 Wasmtime의 47 버전에서 Wasm GC(가비지 컬렉션)와 예외(exceptions) 제안이 모두 기본 활성화됐다. 두 기능 모두 이미 WebAssembly 공식 명세에 병합된 상태이며, 개별 제안 문서는 이제 특정 시점의 아카이브 스냅샷으로 남아 있다. Wasmtime 개발진은 이번 릴리스가 수년에 걸친 엔지니어링과 대규모 런타임 변경의 결과라고 설명했다. 실무자 입장에서 이 변화가 중요한 이유는, 그동안 WebAssembly로 옮기기 까다로웠던 부류의 언어들이 이제 훨씬 쉽고 효율적으로 컴파일 타깃으로 삼을 수 있게 됐기 때문이다.

왜 GC가 런타임 안으로 들어와야 했나

초기 WebAssembly에서는 객체와 참조(objects-and-references) 데이터 모델을 쓰는 고수준 언어들이 자체 가비지 컬렉터를 .wasm 바이너리 안에 통째로 담아야 했다. 원시 포인터와 메모리를 직접 다루는 언어와 달리, 이들은 수집기까지 함께 실어 날라야 했던 것이다. 그 결과 바이너리가 비대해졌고, 네이티브 코드에서 수집기를 구현할 때 흔히 쓰는 기법들, 예컨대 스택 맵과 스택 워킹으로 GC 루트를 식별하는 방식을 쓸 수 없었다. 상당수 언어가 바로 이 처지에 놓여 있었다.

Wasm GC 제안은 여기에 정면으로 대응한다. WebAssembly 언어 자체를 확장해 Wasm 프로그램이 struct와 array 타입, 그리고 서브타이핑 관계를 직접 정의할 수 있게 했다. 예를 들어 이진 트리의 노드 타입을 정의한 뒤 struct.new로 인스턴스를 만들고 struct.get이나 struct.set으로 필드에 접근하는 식이다. 인스턴스의 수명 관리나 수동 해제는 프로그램이 신경 쓸 필요가 없다. 런타임이 전담하기 때문이다. 결과적으로 툴체인은 자체 컬렉터를 넣을 필요 없이 런타임이 제공하는 수집기를 그대로 활용할 수 있고, 바이너리는 가벼워진다.

예외 제안은 예외를 쓰는 언어들에 대해 같은 목표를 갖는다. 이 제안이 없던 시절에는 함수가 정상 반환했는지 예외를 던졌는지를 결과값과 함께 되돌려주는 맞춤형 호출 규약을 툴체인이 직접 구현해야 했다. 모든 호출 지점이 이 조건을 검사하고 분기해야 했으니, 바이너리는 부풀고 가장 흔한 정상 반환 경로에까지 런타임 오버헤드가 얹혔다. 이제는 throw와 try/catch 형태의 구성 요소가 이를 대체하고, 런타임은 정상 반환 경로에 오버헤드를 주지 않는 고전적인 언와인딩 방식으로 예외를 구현할 수 있다. 실행은 빨라지고 바이너리는 작아진다.

선형 메모리 위에 올린 GC 힙

구현을 들여다보면 Wasmtime의 설계 판단이 드러난다. 수집기는 단순한 Cheney 방식의 세미스페이스 복사 컬렉터다. GC 힙을 두 개의 반쪽으로 나눠 한쪽은 새 객체를 할당하는 활성 영역, 다른 한쪽은 유휴 영역으로 두고, 수집 시 살아 있는 객체를 새 활성 영역으로 복사한 뒤 스택 프레임 안의 참조 같은 모든 GC 루트를 새 위치로 갱신한다. 할당은 활성 영역 안에서 포인터를 밀어 올리는 방식이며 읽기·쓰기 배리어가 필요 없다.

주목할 점은 GC 힙을 WebAssembly의 선형 메모리 위에 그대로 올렸다는 것이다. GC 객체 참조는 네이티브 포인터가 아니라 선형 메모리에 대한 32비트 인덱스다. 이 선택은 안전성, 속도, 이식성 세 방향 모두에서 이득을 준다. 안전성 측면에서는, 설령 수집기 버그로 GC 힙이 손상되더라도 악의적인 Wasm 프로그램이 샌드박스를 벗어나 호스트 메모리에 접근할 수 없는 다층 방어가 된다. 속도 측면에서는 가상 메모리 가드 페이지를 활용해 명시적 경계 검사를 없앨 수 있고, 풀링 인스턴스 할당기와 긴밀히 통합돼 5마이크로초대의 인스턴스화 시간을 유지한다. 64비트 머신에서 32비트 참조는 64비트 포인터보다 작아 CPU 캐시를 더 효율적으로 쓴다. 이식성 측면에서도, 여러 플랫폼에서 큰 메모리 영역을 빠르게 다루는 특수 처리를 이미 선형 메모리 구현이 감당하고 있으므로 그 위에 힙을 얹는 편이 유리하다.

개발진은 수집기의 정확성을 끌어올리기 위해 퍼징 인프라도 확장했다. wasm-smith가 GC 제안을 지원하도록 손봐 이론적으로는 GC를 쓰는 거의 모든 Wasm 프로그램을 생성할 수 있게 했고, 시간이 오래 걸릴 수 있는 한계를 보완하기 위해 별도의 퍼저 두 개를 추가로 붙였다. 다만 현재 wasm-smith는 널 불가능(non-nullable) 참조를 생성하지 못하는 사각지대가 남아 있다.

성능 기대치와 다음 단계

실무에 도입하려는 쪽이라면 성능 기대치를 현실적으로 잡아야 한다. 개발진은 지금까지 성능보다 정확성에 집중했다고 분명히 밝혔다. 갓 만든 수집기여서 V8이나 SpiderMonkey처럼 수십 년의 성능 엔지니어링을 거치지 않았고, 처리량과 지연 시간이 당장 그들과 견줄 수는 없다. 또한 이 수집기는 Wasmtime이 프로덕션에서 가장 많이 쓰이는 방식, 즉 작고 일회성인 인스턴스를 대량으로 만들어 각자 소수의 작업을 처리한 뒤 힙째로 버리는 시나리오를 우선 겨냥해 설계됐다. 단일 인스턴스 성능을 극한까지 밀어붙이기보다 여러 인스턴스로 수평 확장하는 데 초점을 맞춘 셈이라, 무기한 살아 있는 장수 서버 프로세스를 튜닝하는 것과는 트레이드오프가 다르다.

앞으로의 작업도 예고됐다. 컴파일러의 별칭 분석 최적화, 이를테면 store-to-load 포워딩이나 중복 로드 제거에 GC 타입 정보를 결합하는 작업이 진행 중이다. 두 타입이 결코 같은 메모리 위치를 차지할 수 없음을 알면 최적화를 더 공격적으로 적용할 수 있기 때문이다. 기능 면에서 다음 큰 이정표는 지연 값 로우어링 위에서 GC와 컴포넌트 모델을 통합하는 것으로, 이것이 완성되면 가비지 컬렉션 언어들이 데이터를 컴포넌트 간에 넘기려고 쓰지도 않는 선형 메모리를 억지로 두는 일 없이 컴포넌트 생태계의 일급 시민이 된다. 정리하면 이번 릴리스는 완성이라기보다, 더 많은 언어를 WebAssembly로 끌어들이기 위한 토대가 마련된 지점에 가깝다.

SOURCE · HACKER NEWS
원문 전체 보기 → https://bytecodealliance.org/articles/wasmtime-gc
SHARE
처리 중...