목록으로

Programming Notes

JetStream 3: 고성능, 연산 집약적 웹 애플리케이션을 위한 현대적인 벤치마크

JetStream 3 이미지

Apple, Mozilla 및 웹 생태계의 여러 파트너와의 긴밀한 협력을 통해 구축된 JetStream 3의 출시를 발표하게 되어 매우 기쁩니다!

JetStream 3 로고

저희는 공동 발표 블로그 포스트를 통해 이번 출시의 개괄적인 내용을 다루었지만, 여기서는 조금 더 깊이 있게 살펴보고자 합니다. 이 포스트에서는 벤치마크 자체의 장막을 걷어내고, 저희가 선택한 방법론을 탐구하며, 이러한 대규모 업데이트를 추진하게 된 동기를 공유하겠습니다.


우리는 왜 벤치마크를 하는 걸까요?

"무엇"을 하는지 알아보기 전에 "왜" 하는지 이야기하는 것이 도움이 될 것입니다. 브라우저 엔지니어들이 벤치마크에 그토록 신경을 쓰는 이유는 무엇일까요?

근본적으로 벤치마크는 성능 저하(regression)가 사용자에게 도달하기 전에 잡아내는 중요한 안전망 역할을 합니다. 하지만 그 이상으로, 벤치마크는 브라우저 엔지니어들에게 일종의 "게임화(gamification)"와 같은 강력한 동기 부여 수단이 됩니다. 명확한 목표가 있으면 노력을 우선순위화하고 어떤 최적화에 집중할지 결정하는 데 도움이 됩니다. 또한 서로 다른 브라우저 엔진 간의 건전한 경쟁을 유도하여 궁극적으로 웹 생태계 전체의 수준을 끌어올립니다.

물론 궁극적인 목표는 단순히 차트의 숫자를 높이는 것이 아니라, 사용자 경험과 실제 환경에서의 성능을 의미 있게 개선하는 것입니다.

개방형 거버넌스에 의한 추진

Speedometer 3와 마찬가지로, JetStream 3는 Apple, Mozilla, Google을 포함한 모든 주요 브라우저 엔진 간의 방대한 협업 노력의 결과물입니다.

저희는 이번 출시를 위해 엄격한 합의 모델을 채택했습니다. 이는 모든 구성원이 가치 있고 대표성이 있다고 동의한 경우에만 새로운 워크로드(작업 부하)를 추가했음을 의미합니다. 이러한 개방형 거버넌스 모델은 여러 당사자의 지지를 이끌어내며 매우 생산적인 협업을 가능하게 했고, 벤치마크가 전체 웹 생태계의 최선의 이익에 부합하도록 보장했습니다.

업데이트가 절실했던 시점

이전 메이저 릴리스인 JetStream 2는 2019년에 출시되었습니다. 기술 분야, 특히 웹 분야에서 6년은 영겁의 시간과도 같습니다.

경제학에는 "측정 수단이 목표가 되는 순간, 그것은 더 이상 좋은 측정 수단이 아니다"라는 '굿하트의 법칙(Goodhart's Law)'이 있습니다. 시간이 흐르면서 엔진은 자연스럽게 벤치마크의 특정 패턴에 맞춰 최적화하게 되고, 그 지표는 실제 성능과의 상관관계를 서서히 잃게 됩니다. Speedometer가 최근 이를 반영해 대규모 업데이트를 진행했듯이, JetStream도 그 뒤를 잇는 것이 당연한 수순이었습니다.

JetStream vs. 다른 벤치마크

최근 Speedometer 3가 출시되었는데 왜 또 다른 벤치마크가 필요한지 궁금하실 수 있습니다.

Speedometer가 UI 렌더링과 DOM 조작을 측정하는 데 탁월하다면, JetStream은 웹 애플리케이션의 연산 집약적인 부분에 초점을 맞춥니다. 브라우저 기반 게임, 물리 시뮬레이션, 프레임워크 코어, 암호화 및 복잡한 알고리즘과 같은 사용 사례를 다룹니다.

실용적인 엔지니어링 측면의 고려 사항도 있습니다. JetStream은 V8의 독립형 셸인 d8과 같은 엔진 셸에서 실행될 수 있도록 설계되었습니다. 엔진 개발자들에게 이는 엄청난 장점입니다. 셸을 빌드하는 것은 Chrome과 같은 전체 브라우저를 컴파일하는 것보다 훨씬 빠르기 때문에 엔지니어가 더 빠르게 반복 작업을 수행할 수 있습니다. 또한 d8은 단일 프로세스이므로 배경 소음(background noise)이 훨씬 적어 더 안정적인 테스트 결과를 얻을 수 있습니다. 이러한 셸 호환성은 시뮬레이터를 실행하는 하드웨어 및 장치 공급업체에게도 JetStream을 매우 가치 있게 만듭니다. 이는 실제 브라우저 환경과는 약간의 괴리가 생기는 트레이드오프(trade-off)이지만, 이를 통해 얻는 엔지니어링 속도의 이점은 그만한 가치가 있습니다.

워크로드 선정 방법

벤치마크를 구축하려면 마이크로벤치마크와 실제 애플리케이션 사이의 섬세한 균형이 필요합니다.

마이크로벤치마크는 훌륭한 엔지니어링 도구입니다. 신호 대 잡음비가 높고 특정 최적화의 효과를 쉽게 확인할 수 있습니다. 새로운 기능의 초기 개선에는 적합하지만, 장기적으로는 과적합(overfitting)을 조장할 수도 있습니다. 엔진이 벤치마크에는 좋게 보이지만 실제 사용자에게는 전혀 도움이 되지 않는 아주 작은 루프 하나에만 과하게 최적화될 수 있기 때문입니다.

이러한 이유로 JetStream 3의 주요 선정 기준은 워크로드가 실제 엔드-투-엔드 사용 사례(또는 최소한 고도로 추상화된 형태)를 대표해야 한다는 것이었습니다.

또한 다양성을 최우선 순위에 두었습니다. 모두가 똑같은 핫 루프(hot loop)를 실행하는 워크로드는 원치 않았습니다. 서로 다른 프레임워크, 다양한 라이브러리, 다채로운 소스 언어 및 고유한 툴체인을 아우르는 커버리지를 목표로 했습니다.

마지막으로 몇 가지 실무적인 기본 규칙을 정했습니다:

  • 시간: 전체 벤치마크 제품군은 몇 분 안에 완료되어야 합니다.
  • 메모리: 저사양 기기에서 충돌이 발생할 정도로 많은 RAM을 소모해서는 안 됩니다.
  • 네트워크: 대규모 페이로드 전송을 요구해서는 안 됩니다.
  • 일관성: 결과는 결정론적(deterministic)이어야 하며 실행할 때마다 반복 가능해야 합니다.

WebAssembly에 대한 재고

JetStream 3에서 가장 중요한 변화 중 하나는 WebAssembly(Wasm)에 대한 집중도를 높이고 대대적인 업데이트를 진행했다는 점입니다.

JetStream 2가 만들어졌을 때 Wasm은 아직 초기 단계였습니다. 오늘날 Wasm은 훨씬 더 널리 보급되었습니다.

언어가 매우 빠르게 진화했기 때문에 JetStream 2는 금세 구식이 되었습니다. JetStream 2는 Wasm MVP(최소 기능 제품)만 테스트했습니다. 하지만 오늘날 Wasm 사양에는 SIMD, WasmGC, 예외 처리(Exception Handling)와 같은 강력한 기능이 포함되어 있으며, 기존에는 이러한 기능들이 제대로 벤치마킹되지 않았습니다.

도구 생태계 또한 완전히 변화했습니다. 기존 워크로드는 거의 전적으로 아주 오래된 버전의 Emscripten으로 컴파일된 C/C++에 의존했으며, 종종 asm2wasm을 통해 더 이상 사용되지 않는 asm.js 백엔드를 활용했습니다. 게다가 일부 오래된 마이크로벤치마크는 잘못된 최적화를 유도하기도 했습니다. 예를 들어, 예전의 HashSet-wasm 워크로드는 실제 사용자 시나리오에서는 오히려 성능을 저하시키는 공격적인 인라이닝(inlining)에 보상을 주는 식이었습니다.

새로운 WebAssembly 워크로드

이를 해결하기 위해 저희는 총 12개의 새로운 Wasm 워크로드를 도입했습니다.

기존 C++뿐만 아니라 J2CL, Dart2wasm, Kotlin/Wasm, Rust, .NET 등 5개의 새로운 툴체인으로 커버리지를 확장했습니다. 즉, 이제 Java, Dart, Kotlin, Rust, C#에서 생성된 Wasm을 활발하게 벤치마킹하고 있습니다!

이 워크로드들은 다음과 같은 실제 엔드-투-엔드 작업을 나타냅니다:

  • argon2: 암호화 비밀번호 해싱 함수.
  • Transformers.js: SIMD를 대거 활용하는 클라이언트 사이드 머신러닝.
  • 교차 플랫폼 UI: WasmGC를 활용하는 Dart 및 Kotlin 워크로드.
  • SQLite3: 기존 WebSQL 패턴을 대체하는 유비쿼터스 데이터베이스.
  • .NET: Wasm 상에 구축된 전체 인터프리터 및 언어 런타임의 예시.

이제 이들은 더 이상 수 킬로바이트 수준의 모듈이 아닙니다. 수 메가바이트에 달하는 애플리케이션으로, 다양하고 복잡한 플레임그래프(flamegraph)를 생성하며 엔진의 한계까지 몰아붙입니다. 현대 웹에서의 중요성이 높아짐에 따라, Wasm은 전체 벤치마크 제품군에서 차지하는 비중이 JetStream 2의 7%에서 15-20%로 늘어났습니다. 새로운 워크로드 외에도 JetStream 3는 인스턴스화뿐만 아니라 런타임 성능이 총점에 정확히 반영되도록 스코어링 방식을 전면 개편했습니다.

새롭고 업데이트된 JavaScript 워크로드

실제 환경에서의 JS 사용 방식을 더 잘 반영하는 새로운 대형 JavaScript 워크로드를 다수 추가했습니다. 단순히 순수 실행 속도만 측정하는 것이 아니라, 파싱(parsing)과 프레임워크 설정 코드를 포함하는 "시작(startup)" 워크로드를 포함하여 초기 페이지 로드 시 발생하는 상황을 더 밀접하게 모방합니다.

  • babylonjs: Babylon.js 3D 엔진의 JavaScript 코어 시작 및 실행.
  • bigint-noble-ed25519: 타원 곡선을 계산하는 BigInt 스트레스 테스트.
  • doxbee: 프로미스(promise)async 함수를 사용하는 비동기 코드 패턴.
  • js-tokens: JavaScript 및 JSX 소스에 대한 js-tokenizer 성능.
  • jsdom-d3-startup: 유닛 테스트에서 자주 사용되는 JavaScript 전용 DOM 구현체에서 실행되는 D3.
  • lazy-collections: JavaScript 제너레이터(generator) 스트레스 테스트.
  • mobx-startup: MobX 상태 관리 라이브러리의 시작 성능.
  • prismjs: 다양한 소스 파일에 대한 구문 강조 라이브러리의 시작 성능.
  • proxies: 서로 다른 라이브러리를 사용하여 프록시(proxy) 기능을 테스트하는 두 개의 새로운 워크로드.
  • raytrace classes: ES6 클래스(classes)의 프라이빗 및 퍼블릭 필드 스트레스 테스트.
  • sync-fs: DataView, 프로미스, 동기 제너레이터/이터레이터를 테스트하는 모의 파일 시스템.
  • threejs: Three.js로 구현된 3D 파티클 시스템.
  • typescript-lib: Typescript v5.9 컴파일 속도.
  • validatorjs: validator.js를 사용한 문자열 검증 및 새니타이제이션(sanitization).
  • web-ssr: React를 사용한 서버 사이드 렌더링(SSR).

업데이트된 JavaScript 워크로드

  • WTB: 다양한 개발자 도구의 성능을 측정하는 web-tooling benchmark의 업데이트 버전.
  • Sunspider: 가중치를 줄이기 위해 모든 개별 워크로드를 단일 항목으로 통합했습니다.
  • 여러 오래된 워크로드의 버그를 수정하고 실제 환경과 맞지 않는 성능 개선 시도를 차단했습니다.

결론

JetStream 3를 통해 브라우저 벤치마킹 분야는 또 한 번 큰 진전을 이루었으며, 브라우저가 소중한 사용자를 위해 성능을 개선할 수 있는 새로운 도구를 갖게 되었습니다. Speedometer 및 MotionMark와 함께 이러한 벤치마크는 브라우저 공급업체뿐만 아니라 사용자에게도 다양한 엔진의 성능에 대한 명확한 뷰를 제공합니다.

자신의 워크로드로 벤치마크에 기여하고 싶거나 개선을 위한 제안이 있다면 언제든지 GitHub 저장소에 참여해 주세요. 저희는 이 벤치마크들을 지속적으로 반복 개선하고 있으며, 앞으로도 지속적인 업데이트를 제공할 예정입니다.