asm.js에 작별을 고하기
(spidermonkey.dev)- Firefox 148부터 SpiderMonkey의 asm.js 최적화가 기본 비활성화됐고, 향후 관련 코드가 제거될 예정
- asm.js는 JavaScript 하위 집합이라 기존 사이트는 계속 동작하지만, 일반 JIT 경로로 실행되어 최적화 이점은 사라짐
- asm.js 콘텐츠는 WebAssembly로 다시 컴파일하면 더 빠른 실행과 더 작은 바이너리를 얻을 수 있음
- asm.js는 NaCl·PNaCl에 맞서 별도 샌드박스나 대체 API 없이 웹 콘텐츠 안에서 네이티브에 가까운 실행을 가능하게 함
- 2013년 Firefox 22에 들어간 OdinMonkey는 Unity·Unreal 같은 C/C++ 웹 배포를 가능하게 했고, WebAssembly로 이어지는 기반이 됨
asm.js 최적화 비활성화
- Firefox 148부터 SpiderMonkey의 asm.js 최적화가 기본 비활성화됐고, 향후 릴리스에서 관련 코드 전체가 제거될 예정
- asm.js를 쓰는 사이트는 계속 동작함
- asm.js는 일반 JavaScript의 하위 집합이어서 기존 코드는 다른 스크립트처럼 일반 JIT 경로로 실행됨
- 다만 asm.js 전용 최적화 이점은 사라짐
- asm.js 콘텐츠를 계속 배포 중이라면 WebAssembly로 다시 컴파일하는 것이 권장됨
- WebAssembly로 다시 컴파일하면 더 빠른 실행과 더 작은 바이너리를 얻을 수 있음
- SpiderMonkey의 WebAssembly 파이프라인은 asm.js 파이프라인보다 훨씬 더 발전해 있음
- WebAssembly가 성공했고 asm.js 사용량 대부분이 이미 이전되면서 두 경로를 함께 유지하는 비용이 커짐
- 유지보수 시간이 계속 필요함
- VM 안에 추가 공격 표면이 남음
asm.js의 역할과 OdinMonkey의 종료
- asm.js는 NaCl과 PNaCl이 던진 “웹에서 네이티브 속도로 코드를 실행할 수 있는가”라는 질문에 대한 Mozilla의 답이었음
- 엔진이 즉석에서 인식할 수 있는 엄격하고 정적으로 타입이 지정된 JavaScript 하위 집합을 골라 네이티브 코드로 컴파일하는 방식
- 별도 샌드박스, IPC, 대체 API 없이 웹 콘텐츠 안에 머물면서 기존 웹 API를 사용할 수 있었음
- asm.js는 2013년 Firefox 22에 포함됐고, Unity와 Unreal 같은 프로젝트가 표준 웹 기술만으로 C/C++ 코드베이스를 웹에 배포할 수 있게 해줌
- Epic Citadel demo는 4일 만에 웹으로 포팅됨
- asm.js는 웹 기술만으로 거의 네이티브에 가까운 속도의 코드를 실행할 수 있음을 입증했고, 이후 WebAssembly로 이어지는 길을 열었음
- WebAssembly는 몇 년 뒤 Firefox 52에 포함됐으며, asm.js가 없었다면 WebAssembly도 없었을 가능성이 큼
- asm.js 컴파일러의 이름은 OdinMonkey였고, 제거 작업은 Ragnarök 버그에서 “Twilight of OdinMonkey”로 추적됨
- OdinMonkey에서 태어난 BaldrMonkey는 WebAssembly 최적화 컴파일러
- RabaldrMonkey는 WebAssembly 베이스라인 컴파일러
- OdinMonkey는 13년간의 역할을 마치고 종료 수순에 들어감
댓글과 토론
Hacker News 의견들
-
asm.js는 NaCl과 PNaCl이 던진 “웹에서 네이티브 속도로 코드를 실행하려면?”이라는 질문에 대한 Mozilla의 답이었음
지금이었다면 Chrome은 NaCl과 PNaCl을 그냥 밀어붙였을 것이고, 그러면 모두가 Safari와 Firefox가 왜 “웹” 표준을 따라오지 못하냐고 불평했을 듯함- 아직도 우리가 잘못된 시간선에 있다고 봄. PNaCl은 죽었고, 제때 등장한 достой한 후계자 대신 Electron 앱 수프 속에서 산 채로 삶아지는 상황이 됐음
한동안은 모든 일을 브라우저에서 하게 될 거라고 진지하게 생각했음. 어떤 의미에서는 점점 그렇게 되고 있지만, 전반적인 체감은 어느 때보다 나빠졌음. WASM은 좋아하고 좋아하고 싶지만, 생태계의 성숙 속도는 믿기 어려울 정도로 형편없음
더 나쁜 건 신뢰할 수 없는 AI 도구와 그 출력물을 바로 그런 샌드박스 안에서 실행해야 하는데, 회사들은 정반대로 호스팅 샌드박스와 호스팅 JS 기반 VM을 팔고 있다는 점임
결국 항상 문제는 같았던 듯함. 클라이언트 쪽 샌드박스에는 돈이 없었음 - 기억하는 바로는 그렇지 않았음. 당시에는 유용했던 실험이었지만 결국 잘 풀리지 않았음
내부적으로는 Flash 플레이어를 샌드박스화하는 데 유용했지만, LLVM 기반 접근의 한계가 곧 관련자 모두에게 분명해졌음 - 사실 그때도 기본적으로 그렇게 하려 했음. 웹 표준 위원회들을 통해 통과시키려 했고 절차도 밟았음
기억이 맞다면 실패한 큰 이유는 NaCl이 너무 “큰” 기술이었고 asm.js는 너무 “작은” 기술이어서, asm.js가 몇 년 늦게 시작했음에도 먼저 프로덕션 준비 상태에 도달했기 때문임 - Chrome이 밀어붙이고, Apple은 WebKit 팀에 투자하지 않으면서 아무 말도 안 하는 식으로 시간을 끌고, 인터넷의 잘 속는 사람들이 Apple 쪽에 맞춰줬을 거라는 뜻일 듯함
다만 Apple은 규제 문제가 본격화된 이후 이번 10년 들어 WebKit 투자를 늘린 것 같아서, 오늘날이라면 결과가 다를 수도 있음 - 웹 플랫폼에 있으면 유용할 기능에 대해 높은 호환성의 대안이 없을 때 불평이 나옴. asm.js와 그 뒤의 Wasm이 바로 그런 대안이었음
그래서 당시에는 불평이 적었고, 지금은 App Store에 해가 된다는 이유로 Safari가 하지 않는 것들에 대해 불평이 나오는 것임. 다행히 EU가 이제 Apple을 어느 정도 바로잡으려 하고 있음
- 아직도 우리가 잘못된 시간선에 있다고 봄. PNaCl은 죽었고, 제때 등장한 достой한 후계자 대신 Electron 앱 수프 속에서 산 채로 삶아지는 상황이 됐음
-
슬프지만 납득은 됨. 재미있는 사실로, Figma는 원래 완전한 C++ 코드베이스로 시작했고, asm.js는 디자인 도구를 브라우저에서 실행할 수 있음을 입증하는 핵심이었음
WebAssembly로 전환한 건 유료 고객이 생긴 뒤였고, 로드 시간 개선도 꽤 컸음. asm.js는 여전히 JS라 번들 크기가 더 크고 코드를 AST로 파싱해야 하지만, WASM은 그렇지 않음- 뭐가 그렇게 슬픈지 모르겠음. 한때 의미 있었던 컴파일 대상일 뿐임
i386-unknown-freebsd1 지원이 빠졌다고 슬퍼하는 것과 비슷함 - 슬픈 건 깔끔한 네이티브 데스크톱 Figma 앱을 가질 수도 있었다는 점임
- 뭐가 그렇게 슬픈지 모르겠음. 한때 의미 있었던 컴파일 대상일 뿐임
-
이제 asm.js의 죽음이 온 건가? 예언의 시간선에서 멀어지고 있음
https://www.destroyallsoftware.com/talks/the-birth-and-death...
아직 본 적 없다면 강력 추천함. “최고”의 정의에 따라서는 역대 최고의 기술 발표일 수도 있음- 이 기술의 죽음으로 예언의 실이 끊어졌음. 저장된 게임을 불러와 운명의 직조를 복원하거나, 스스로 만든 파멸한 세계에서 계속 버티면 됨
- 그 발표는 1년에 두세 번 다시 봄. 발표를 어떻게 해야 하는지, 슬라이드 덱을 발표와 어떻게 맞춰야 하는지 보여주는 훌륭한 예이고, 운영체제의 권한 링 구조를 의외로 교육적으로 훑어줌
언젠가 전쟁의 시기를 거치고 낡은 프로그래밍 패러다임에 대한 심리적 집착이 풀리면 더 진보한 방식으로 넘어갈 수 있을 것임. 물론 그렇다고 당신의 은행이 앞으로 최소 85년간 YavaScript를 돌리지 않게 되지는 않음 - asm.js를 WASM으로 바꿔 넣으면 여전히 맞는 길에 있음
- 걱정할 것 없음. YavaScript는 영원히 살아남을 것임
- 전쟁을 COVID로 바꾸면 둘 다 2020년에 일어났으니 크게 벗어나지 않음. 진짜인지 보려면 아직 2035년까지 기다려야 함
-
Gary Bernhardt의 JavaScript 발표를 봤던 순간을 절대 잊지 못함.[0] 그때 asm.js를 처음 알게 됐고, 브라우저에서 실행되도록 코드를 컴파일하는 토끼굴에도 빠졌음
12년이 지난 지금, 그의 픽션 중 얼마나 많은 것이 현실이 됐는지 놀라움
[0] https://www.destroyallsoftware.com/talks/the-birth-and-death...- “두꺼운 앱(thick apps)” 부분이 늘 기억에 남았음. 이름이 웃기기도 했고, 내 상황과도 꽤 가까웠기 때문임
그 발표를 처음 봤을 때는 인턴이었고, 회사는 작은 산업용 IO 박스용으로 컴파일러, IDE, 디버거를 전부 자체 제작했음. 컴파일러는 C로 작성됐고, Emscripten으로 JS로 바꾼 뒤 IDE와 디버거 부분과 함께 거대한 HTML 파일로 “컴파일”(사실상 이어붙이기)했음. 코드 업로드와 디버깅은 이더넷으로 했고, 전부 Ajax로 밀어 넣었음
저주받은 구조처럼 들리지만 고객들은 좋아했음. EXE가 아니어서 고객사들의 과도한 기업 IT 필터에 걸리지 않았기 때문임. 그래서 Electron으로 전환하지 않았음. 어떤 의미에서는 두꺼운 앱의 원시적 요소를 갖고 있었던 셈임 - AI의 부상이 없었다면 WASM이 모든 언어를 위한 기계 수준 컴파일 대상이 됐을 가능성도 있음. Gary가 많은 것을 예측했지만 AI는 보지 못했음
- “두꺼운 앱(thick apps)” 부분이 늘 기억에 남았음. 이름이 웃기기도 했고, 내 상황과도 꽤 가까웠기 때문임
-
오래전에 WebGL 책에서 asm.js에 관한 작은 장을 쓴 적이 있음
https://webglinsights.github.io/
WebAssembly의 전신이었던 asm.js의 부상을 보는 건 즐거웠음. 초기 데모 중에는 브라우저에서 Unreal Engine이 돌아가는 것처럼 정말 멋진 것들이 있었음. 여기서 해가 지는 걸 보니 씁쓸하지만, 훨씬 더 나은 것들로 이어졌음 -
asm.js에서 WASM으로 가는 트랜스파일러가 필요할지도 모름
레거시 버전 Emscripten으로 레거시 코드를 컴파일하는 건 꽤 답답함. 누적된 Emscripten ABI 변경에 맞춰 JS 코드를 갱신하는 것만큼이나 괴로울 때도 있음- Binaryen에 예전에는 asm2wasm 도구가 있었지만, 이제는 폐기된 것으로 알고 있음. 다른 동등한 도구는 찾지 못했음
적어도 asm.js 최적화가 꺼져도 asm.js 코드는 계속 동작하겠지만, 변환기가 있으면 좋겠음 - https://porffor.dev/
- Binaryen에 예전에는 asm2wasm 도구가 있었지만, 이제는 폐기된 것으로 알고 있음. 다른 동등한 도구는 찾지 못했음
-
asm.js는 죽었다! WebAssembly 만세!
- 공평하게 말하면, wasm이 등장하면서 asm.js는 몇 년 전에 이미 폐기 예정이 된 줄 알았음
-
개인적으로는 이 결정이 실수라고 봄. 다만 실제 영향이 얼마나 클지는 모르겠음. 내가 알기로 아직 asm.js를 쓰는 사람이 많지는 않음
하지만 wasm은 JavaScript와 너무 격리돼 있음. 제한적으로 써본 뒤에는 차라리 asm.js로 컴파일해볼까 생각했음
Emscripten이 아직 완전히 지원하는지도 확신이 없었음
wasm에서는 대부분의 웹 API를 호출할 수 없음
내가 하려던 작업에서는 더 중요하게, JS에서 wasm으로 제로 카피 버퍼를 넘길 수 없음
모든 것은 절충임. 격리는 장점이지만 단점이기도 함- asm.js는 JS와 상호작용하는 면에서 wasm보다 확실히 더 제한적일 것임. 기본적으로 단순 숫자 값과 배열 버퍼 정도로 제한됨
반면 요즘 wasm은 GC 타입이 있고 externref로 JS 값을 붙잡아둘 수도 있음
asm.js에서도 제로 카피 버퍼는 못 할 가능성이 큼. wasm 쪽에는 제로 카피 버퍼 제안이 있음: https://github.com/WebAssembly/memory-control/blob/main/prop... - 기억이 맞다면 Emscripten은 2020년쯤 asm.js 지원을 제거했고, asm.js를 지원했던 가장 중요한 도구 체인이었을 것임. 그다음은 Rust일 수도 있는데 아직 지원하는지는 모르겠음
엄격한 asm.js에서도 대부분의 웹 API를 직접 호출할 수 없음. 숫자만 지원하고 JS 문자열이나 객체는 지원하지 않으며, C 힙을 WASM처럼 ArrayBuffer 객체 안에서 관리하기 때문임
제로 카피 버퍼도 같은 문제임. asm.js에서 “진짜” JavaScript로 빠져나가 호출해야 하고, 다른 ArrayBuffer를 asm.js 힙에 직접 매핑할 수도 없어서 복사가 필요함. “JavaScript FFI”는 asm.js와 WASM 사이에 사실상 크게 다르지 않음 - 내가 오해한 게 아니라면 asm.js도 같은 제약이 있지 않나? 즉 asm.js 코드에서 웹 API를 직접 호출할 수 없고, “외부” 함수에 대한 별도 처리가 여전히 필요함
- asm.js는 JS와 상호작용하는 면에서 wasm보다 확실히 더 제한적일 것임. 기본적으로 단순 숫자 값과 배열 버퍼 정도로 제한됨
-
Mozilla가 asm.js 코드에 극도로 특화된 OdinMonkey를 공개했을 때가 기억남. Chrome/V8 팀은 대신 일반 JavaScript를 더 빠르게 실행하면서 asm.js에도 도움이 되는 범용 JIT 최적화에 집중했음
속도 차이는 Firefox가 2~4배 유리했고, 그걸 꽤 크게 홍보했음 :D
요즘은 대부분의 브라우저 JavaScript VM이 매우 비슷한 설계와 최적화로 수렴해서, Odin이 없어도 asm.js 코드는 어쨌든 꽤 빠르게 실행될 것임 -
런타임에 JS 코드를 생성해서 알고리즘을 더 빠르게 만들려던 내 계획은 사라졌음. 이걸 wasm으로 하기는 훨씬 어려울 듯함
- 런타임에 wasm 코드를 생성하는 건 꽤 쉬움. 유효한 asm.js 코드를 생성하는 것보다 더 쉬울 수도 있음
테스트용으로 그런 작업 상당 부분을 처리하는 작은 라이브러리가 있음: https://searchfox.org/firefox-main/source/js/src/jit-test/li... - 라이브러리를 쓰면 더 어렵지 않음. Rust에서는 이런 걸 쓰면 됨: https://crates.io/crates/wasm-encoder
JS에서는 아마 https://www.npmjs.com/package/binaryen을 쓰게 될 것임 - 아직 AssemblyScript가 있음. 내가 오해했거나 그 기능을 잘못 알고 있는 게 아니라면 요구사항에 맞을 수도 있음
https://www.assemblyscript.org/ - 그냥 asm.js 부분집합을 써보고 성능이 어떤지 보면 됨. 브라우저의 특별한 asm.js 지원이 없어도 Emscripten 출력물 성능이 놀랄 만큼 좋았던 것으로 기억함
- 여전히 동작할 것임. asm.js는 결국 일반 JavaScript 코드임
다만 asm.js 전용 파이프라인처럼 빠르게 파싱되거나 실행되지는 않을 것임. 아주 거대한 애플리케이션이 아니라면 차이를 크게 느끼지 못할 것 같음
- 런타임에 wasm 코드를 생성하는 건 꽤 쉬움. 유효한 asm.js 코드를 생성하는 것보다 더 쉬울 수도 있음