Google Sheets는 Chrome에서 WasmGC를 사용하는 Google의 첫 번째 제품 중 하나입니다. 이 이전은 2022년에 발표되었으며, Sheets 및 Chrome팀은 최적화에 대한 실시간 피드백을 제공하기 위해 표준화, 엔지니어링, 도구에 대해 협력했습니다. 이 파트너십은 Google 엔지니어링팀이 Chrome과 효과적으로 협력하여 더 많은 Google 앱을 WasmGC에서 실행할 수 있는 선례를 만들었습니다.
챌린지: JavaScript
Google Sheets 계산 엔진은 원래 Java로 작성되었으며 2006년에 출시되었습니다. 제품 초기에는 모든 계산이 서버에서 이루어졌습니다. 하지만 2013년부터는 JavaScript를 사용하여 브라우저에서 엔진이 실행됩니다. 이 작업은 원래 Google 웹 도구 (GWT)를 통해 수행되었으며, 나중에는 Java에서 Closure JavaScript 트랜스파일러 (J2CL)를 통해 수행되었습니다. JavaScript 계산 엔진은 Web Worker에서 실행되며 MessageChannel를 사용하여 기본 스레드와 통신합니다.
사용자를 서버에서 JavaScript 버전의 계산 엔진으로 (나중에는 GWT에서 J2CL로) 이전하는 작업은 신중한 검증이 필요한 주요 작업이었습니다. JavaScript 계산 엔진이 Java 버전과 정확히 동일한 결과를 생성하도록 하기 위해 Sheets팀은 내부 검증 메커니즘을 개발했습니다. 이 메커니즘은 대규모 시트 코퍼스를 처리하고 여러 버전의 계산 엔진 간에 결과가 동일한지 확인할 수 있습니다. Sheets팀은 이 도구를 정기적으로 사용하여 Sheets의 변경사항을 검증합니다. 하지만 팀은 이러한 계산 결과를 비교했을 뿐만 아니라 클라이언트의 JavaScript와 서버의 Java 간 성능도 비교했습니다. 계산 엔진의 JavaScript 버전이 Java 버전보다 3배 이상 느린 것으로 나타났습니다.
JavaScript가 Java보다 느린 이유는 무엇인가요?
JavaScript는 느슨한 유형의 동적 언어에 비해 빠릅니다. 지난 15년 동안 적시 (JIT) 컴파일러 (예: Maglev, Sparkplug, Turbofan)에 대한 대대적인 투자를 통해 JavaScript의 성능이 향상되었습니다. 하지만 JavaScript의 느슨한 유형과 동적 동작으로 인해 JIT 컴파일러가 최적의 코드를 생성하기가 어렵습니다. 즉, JavaScript는 원시 처리량에서 Java 및 C++과 같은 언어에 비해 여전히 뒤처집니다. TypeScript는 JavaScript에 유형 안전성을 추가하지만, 이 유형 정보는 개발을 더 쉽게 하도록 설계되었으며 컴파일러가 최적의 코드를 생성하는 데 필요한 보장을 제공하지는 않습니다. 대규모 스프레드시트를 계산하는 데 수십 초가 걸릴 수 있는 Google Sheets와 같은 경우 JavaScript는 빠르지만 충분히 빠르지는 않습니다.
솔루션: WasmGC
WasmGC는 가비지 수집 언어 (예: Java)를 컴파일하는 데 필요한 기본 요소를 추가하는 기존 WebAssembly 사양의 확장 프로그램입니다. 예를 들어 WasmGC는 유형을 정의하고 가비지 수집된 데이터 구조를 할당하는 명령어를 추가합니다. WasmGC는 가비지 수집 언어에 대해 Wasm이 C++ (예: Photoshop 또는 Google Earth)에 대해 한 것과 같은 역할을 할 것으로 예상됩니다. 즉, 네이티브 속도에 가까운 속도로 웹에 제공하는 것입니다. Google에서는 가비지 수집 언어의 인기로 인해 WasmGC가 Wasm보다 더 큰 영향을 미칠 수 있다고 생각합니다.
Chrome을 사용하는 Google Workspace 파트너
WasmGC MVP 초안 사양은 2019년에 게시되었습니다. 2020년 말 Google Workspace와 Chrome은 Sheets 계산 엔진을 사용하여 WasmGC를 평가하기 위해 파트너십을 맺었습니다. Workspace의 멀티 플랫폼 팀은 컴파일러와 트랜스파일러를 빌드하고 최적화하는 데 상당한 전문성을 보유하고 있습니다. Workspace의 일부인 Sheets는 성능에 민감하고 강력한 성능 및 정확성 검증 메커니즘을 갖추고 있어 WasmGC를 평가하는 데 이상적인 후보로 확인되었습니다. Chrome에는 WasmGC 런타임을 빌드하고 최적화하는 V8 팀과 사전 (AOT) 최적화를 빌드하는 Binaryen 기여자가 있습니다. Chrome과 Workspace에는 WasmGC 도구 체인을 빌드하고 최적화하는 데 필요한 모든 전문 지식이 있으며 Google Sheets는 이상적인 테스트 환경입니다.
첫 번째 프로토타입
2021년 중반까지 팀은 작동하는 Java to WasmGC 컴파일러를 개발했습니다. 같은 해 말에는 WasmGC로 실행되고 계산을 수행하는 Google Sheets의 프로토타입 버전이 있었습니다. 그 과정에서 많은 어려움에 직면했습니다. 프로파일링 및 힙 덤프를 위한 도구가 없어서 빌드해야 했습니다. 기존 구현은 대체하거나 WasmGC용으로 작성해야 하는 JavaScript 라이브러리를 많이 사용했습니다. 사양, 컴파일러, 새 라이브러리의 실험적 특성으로 인해 Wasm 계산 엔진의 정확성을 검증하는 데 시간이 많이 걸렸습니다. 하지만 Sheets의 유효성 검사 메커니즘은 다시 한번 매우 유용했습니다. 결국 팀은 모든 것을 작동시켰고 2022년 초부터 실적 데이터가 들어오기 시작했습니다.
추가 최적화
Sheets Wasm의 초기 버전은 JavaScript보다 계산 성능이 약 두 배 느린 것으로 나타났습니다. 하지만 새로운 사양, 새로운 컴파일러, 여러 새로운 라이브러리를 고려하면 나쁘지 않은 결과입니다. 이때부터 Sheets팀은 최적화를 시작했습니다. Google은 발견된 최적화 중에서 다음과 같은 몇 가지 카테고리를 파악했습니다.
- Java 가상 머신 (JVM) 및 V8에 이미 있던 핵심 최적화를 복제합니다.
- 고도로 최적화된 브라우저 API 사용
- JavaScript 관련 코딩 패턴 삭제
먼저 Sheets팀은 다른 도구 모음에 이미 있는 최적화를 복제해야 했습니다. 이러한 최적화의 가장 좋은 예는 가상 메서드 디스패치를 최적화하는 것입니다. 이는 JVM과 V8에서 오랫동안 최적화해 왔지만 WasmGC에는 아무것도 없었습니다. 추측 인라이닝과 가상화 해제라는 두 가지 매우 일반적인 최적화를 구현한 결과 Chrome에서 계산 시간이 약 40% 빨라졌습니다.
둘째, 브라우저 API가 최적화된 네이티브 구현으로 지원되는 경우 Wasm을 사용하여 경쟁하기 어려울 수 있습니다. 문자열과 정규 표현식이 좋은 예입니다. 특히 정규식의 경우 re2j (WasmGC로 컴파일됨)에서 각 정규식을 자체 머신 코드로 컴파일할 수 있는 Chrome의 RegExp 브라우저 API로 전환할 때 정규식 작업 속도가 거의 100배 빨라졌습니다.
마지막으로 수년간의 최적화로 인해 코드베이스가 JavaScript에 과적합되었다는 사실을 알게 되었습니다. 예를 들어 배열과 맵 간의 경계를 모호하게 만드는 핵심 데이터 구조가 Sheets에 있었습니다. 이는 희소 배열을 맵으로 자동 모델링하는 JavaScript에서는 효율적이지만 다른 플랫폼에서는 느립니다. 따라서 플랫폼에 구애받지 않는 방식으로 코드를 다시 작성해야 했습니다. 팀이 WebAssembly를 좋아하는 또 다른 이유는 멀티플랫폼 애플리케이션이 웹에서 우수한 성능을 얻기가 더 쉽기 때문입니다. JavaScript의 특성에 맞게 전체 애플리케이션을 수정할 필요가 없습니다.
최종 결과
이러한 모든 최적화 후 최종 WasmGC 버전의 Sheets는 계산 성능이 JavaScript보다 약 2배 빠르며, 이는 초기 WasmGC 버전의 시작점보다 4배 개선된 것입니다.
결론
WasmGC는 개발자가 웹 애플리케이션을 빌드하는 방식을 발전시킬 수 있는 강력한 기술입니다. 향후 몇 년 동안 Google에서는 WasmGC가 공유 메모리 멀티스레딩을 지원하고 단일 스레드 성능을 더욱 개선할 수 있기를 바랍니다. 모든 웹 개발자는 다음 고성능 프로젝트에 WasmGC 사용을 고려해 보는 것이 좋습니다. 함께 웹을 더 빠르고 원활하게 만들어 보세요.
감사의 말씀
WasmGC 구현 및 이 사례 연구에 참여해 주신 다음 분들께 감사드립니다. Diwas Adhikary, Matthew Albright, Ksenia Bukina, Julien Dramaix, Asim Fazal, Michael Frederick, Goktug Gokdogan, Janice Gu, Adam Klein, Manos Koukoutos, Jakob Kummerow, Matthias Liedtke, Thomas Lively, Roberto Lublinerman, Vishrut Mehta, Thomas Nattestad, Josh Pearlstein, Joaquim Perotti, Chris Ruenes, Steven Saviano, Derek Schuff, Tim Sears, Michael Thomas, Yuan Tian, Philipp Weis, Mason Wu, Alon Zakai, Emanuel Ziegler