Outsider's Dev Story

Stay Hungry. Stay Foolish. Don't Be Satisfied.
RetroTech 팟캐스트 44BITS 팟캐스트

asm.js에 대해서

asm.js을 처음 듣게 된 것은 존 레식이 올린 다음 트윗을 보고나서였다.


처음에는 어떤 자바스크립트 라이브러리가 나왔길래 존레식이 저렇게 흥미를 가지나 싶어서 좀 살펴봤는데 asm.js은 일반적으로 js가 붙는 라이브러리와 완전히 다른 것이었다. C는 잘 모르기 때문에 asm.js을 정확하게 이해하기는 좀 어려웠고 asm.js가 아직 초기단계인 관계로 자료가 많지 않아서 정확히 파악하고 테스트해보기도 좀 어려웠다. 일부 틀린 내용이 있을 수도 있지만 파악한 내용위주로 정리를 해본다.


asm.js는 무엇인가?
asm.js는 파이어폭스 팀에서 만든 것인데 파이어폭스의 자바스크립트 엔진이 SpiderMonkey인데 내부적으로는 여러 가지로 변화가 있었다. TraceMonkey에서 JagerMonkey를 지나서 지금은 IonMonkey를 쓰고 있는 데 차후 파이어폭스에서는(22버전으로 6월정도에 배포될 것이다.) OdinMonkey가 들어가게 되고 이 OdinMonkey가 asm.js를 위한 컴파일러이다.

Emscripten이라는 프로젝트가 있는데 LLVM이 생성한 바이트코드를 자바스크립트로 변환해 주는 프로젝트다. Emscripten를 사용하면 다른 언어로 작성된 코드를 자바스크립트로 변환해서 돌릴 수 있다.(당연히 아무거나 변환한다고 되는건 아니다.) asm.js은 개념을 좀더 발전시킨 것으로 보이는데 Emscripten이나 Mandreel같은 컴파일러가 생성한 자바스크립트의 서브셋을 스팩화했다고 할 수 있다. 실제로도 asm.js를 찾아보면 자바스크립트 서브셋이라고 정의하고 있다. (Emscripten로 C/C++ 코드를 자바스크립트로 컴파일하려면 -O2 -s ASM_JS=1 옵션을 사용해야 한다.)

asm.js의 목적은 성능향상이라고 할 수 있는데 이해한 바를 아주 간단히 정리하면

고수준의 추상기능은 원래처럼 사용하지만 대신 산술연산은 네이티브 코드처럼 OdinMonkey가 최적화해서 성능을 향상시킨다.

AOT(ahead-of-time) 컴파일러로 asm.js코드로 사용가능한 부분을 극도로 제한해서 성능개선을 하도록 한 것이고 또한 예측가능하다. 그래서 asm.js는 "use asm"이라는 디렉티브를 선언하고 asm.js를 지원하는 자바스크립트엔진이 "use asm" 디렉티브를 만나면 해당 코드를 어셈블리로 변환한다. 이는 Strict mode의 "use strict"와 같이 동작한다. 그래서 asm.js를 지원하지 않는 엔진에서는 "use asm"은 그냥 문자열일 뿐이므로 무시하게 되고 최적화되지 않은 코드가 그냥 동작할 뿐이다. 그러므로 asm.js는 현존하는 자바스크립트엔진에서도 그대로 사용할 수 잇다. 즉, asm.js를 지원하는 AOT 컴파일엔진과 그렇지 않은 엔진에서 동작은 동일하지만 성능만 다르다.(현재는 OdinMonkey만 지원하고 있지만 V8에도 asm.js 지원에 대한 이슈가 올라온 상태이다.)


asm.js는 어떻게 동작하는가?
이 부분은 완전히 이해도 못했고 자세한 내용도 많이 공개되지 않았다. 대충만 설명하면 asm.js코드는 다음과 같이 생겼다.

i = i + 12 | 0;
f = e | 0;
h = d + 12 | 0;

자바스크립트이기는 하지만 일반적으로 사용하는 자바스크립트와는 약간 다르다.

  • 앞에서도 얘기했듯이 asm.js는 숫자타입만 다루고 다른 타입은 다룰 수 없다.
  • 모든 외부 데이터는 유일한 힙 객체에 typed array 힙 객체에 모두 저장된다.
  • 변수에 접근하거나 설정할 때는 특정타입으로 일관성있게 강제한다.
  • 컴파일해야하는 asm.js에는 "use asm" 디렉티브를 사용해서 명시적으로 지정한다.
  • "use asm" 디렉티브를 만나서 asm.js 코드를 처리할 때 엄격한 유효성검사를 통과하지 못하면 이는 무시한다.(오류는 아니고 콘솔에 경고를 출력한다.)
이러한 과정을 통해서 인터프리팅없이 바로 어셈블리로 처리할 수 있게 되고 이러한 제약을 통해 자바스크립트를 느리게 만드는 요소들을 효율적으로 제거한다. 이렇게 하면 왜 빨라지는가는 설명하기도 어렵고 설명할만큼 이해하지도 못했기 때문에 그냥 그렇다는 거만 얘기하고 넘어간다..(ㅡㅡ;;)

function DiagModule(stdlib, foreign, heap) {
  "use asm";

  // 변수 선언
  var sqrt = stdlib.Math.sqrt;

  // 함수 선언
  function square(x) {
    x = +x;
    return +(x*x);
  }

  function diag(x, y) {
    x = +x;
    y = +y;
    return +sqrt(square(x) + square(y));
  }

  return { diag: diag };
}

존 레식이 올린 Asm.js: The JavaScript Compile Target라는 글에서 가져온 위의 코드를 보자. "use asm"; 디렉티브로 인터프리터이 이 코드를 어셈블리로 변환한다. 그리고 다른 자바스크립트에서 이 DiagModule을 사용할 수 있다.(현재 asm.js가 아닌 곳에서 asm.js를 호출하거나  asm.js내에서 외부 코드를 호출했을때 집입하고 빠져나오는 루틴으로 인해 다른 호출보다 느린 문제가 있다고 한다. 이문제를 해결하려고 노력중이라고는 하는데 그래서 현재 성능을 확인해 보려면 asm.js 내에서만 호출이 이뤄지도록 해야한다.)

asm.js FAQ를 보면 컴파일비용에 대한 언급이 나온다. 유효성감사를 하고 컴파일하는 과정을 아주 빠르긴 하지만 당연히 컴파일비용이 든다.(asm.js 코드에만 해당된다.) FAQ에서는 eval이나 Function을 사용한 동적 컴파일을 통해서 이 비용을 뒤로 미룰 수도 있음을 언급하고 있고 차후에 백그라운드에서 컴파일하고 컴파일 결과를 스토리지에 저장했다가 재사용하는 방법도 계획중임을 밝히고 있다. Alon Zakai의 Big Web App? Compile it!라는 발표자료를 보면 C/C++외에 다른언어도 컴파일할 수 있음을 밝히고 있다. C/C++는 꽤 진행되었지만 Java, C#, Objective-C는 괜찮은 편이고 Python이나 Ruby, Lua등은 아직 작업이 더 필요하다고 하는데 이부분이 전체적인 자바스크립트를 타ㅋ겟으로 한 컴파일얘기인지 asm.js에 국한된 얘기인지는 더 확인해 봐야 할 것 같다.


성능
현재 파이어폭스 나이틀리에는 이미 OdinMonkey가 적용되었다. 22버전이 발표되던 시점에 윈도우/리눅스용에만 적용되었다고 되어 있는데 지금은 23알파까지 나온 상태이다.(OS X는 언제 지원되는지 아직 잘 모르겠다.) 

사용자 삽입 이미지

파이어폭스 나이틀리에서 about:config를 보면 asmjs가 추가된 것을 확인할 수 있다. 현재 asm.js를 테스트해볼 수 있는 BananaBench를 내 PC(윈도우)에서 돌려보면 다음과 같은 결과가 나온다.

파이어폭스 23.0a1의 BananaBench 결과

파이어폭스 23.0a1의 BananaBench 결과

크롬 26의 BananaBench 결과

크롬 26의 BananaBench 결과

BananaBench는 FPS같은 게임을 돌려보면서 벤치마킹하는 페이지인데 위처럼 파이어폭스의 성능이 더 높은 걸을 볼 수 있고 실제로 돌려보면 화면전환이 눈으로 보아도 더 자연스럽다. 다음은 또 하나의 테스트인 ammo.js benchmark이다.

파이어폭스 23.0a1의 BananaBench 결과

파이어폭스 23.0a1의 BananaBench 결과

크롬 26의 BananaBench 결과

크롬 26의 BananaBench 결과

ammo.js benchmark는 물리엔진을 사용해서 큐빅이 떨어지면서 벤치마크를 하는 페이지인데 큐빅을 2500으로 설정했을 때 파이어폭스는 9~12정도가 나오지만 크롬에서는 4-7정도가 나오는 것을 볼 수 있다.

다음은 Alon Zakai가 발표한 BIG WEB APP? COMPILE IT!라는 발표자료에서 가져온 벤치마크 자료이다.

asm.js의 벤치마크 자료
asm.js의 벤치마크 자료
벤치마크는 언제나 여러 이견이 있을 수 있고 또 asm.js가 아직 초기단계라는 점에서 벤치마크자료는 그냥 참고용으로만 보는게 좋지만 그래도 성능향상의 폭이 무척 큰 것을 볼 수 있고 시간이 지나면서 더욱 향상될 것으로 기대할 수 있다. 많은 글에서 얘기하기로는 clang을 사용한 네이티브 C 프로그램보다 2배정도 느리다.(네이티브에 비교한 것이므로 빠르다는 얘기다.)


asm.js로 인한 영향은?
asm.js으로 기대해 볼 수 있는 부분을 보기위해서 좀더 테스트를 직접 해보고 싶지만 아직 자료가 많지 않아 이것저것 해보려면 더 많은 연구(혹은 삽질)을 해보아야 할것 같다. asm.js가 하려는 시도 등을 보았을 때 일반적으로 웹개발을 하면서 자바스크립트를 사용하는 개발자들이 asm.js을 직접 사용할 일은 그리 없어보인다. 일반 코드에 "use asm"을 쓴다고 성능이 막 올라가고 그런게 아니라서...(이런 부분은 더 테스트해보아야 할것 같다.) 그보다는 기본에 웹으로는 구현하기 어려웠던 아주 큰 어플리케이션은 asm.js로 접근해 볼 수 있을 것으로 보안다. 더욱이 asm.js는 직접 작성하도록 만들어진게 아니라 C/C++같은 프로그램을 컴파일해서 사용하도록 만들어진 것이므로 일반적인 개발자들이 asm.js을 좀더 활용하려면 asm.js로 컴파일할 수 있는 중간언어가 필요해진다. 존 레식의 언급대로라면 LLJS가 현재로서는 가장 좋은 후보라고 한다.



위 영상은 많이들 보았겠지만 모질라팀이 공개한 Epic의 언리얼 엔진 3를 asm.js를 이용해서 웹으로 포팅한 것이다. 아직 직접 돌려볼 수 있는 것은 공개되지 않았지만 동영상으로는 상당한 속도나 나오는 것을 볼 수 있다. 일단 당장 생각할 수 있는 것은 성능의 한계가 컸던 그래픽부분에서 asm.js를 이용할 수 있을듯 하지만 이제 막 등장한 기술이기 때문에 활용분야는 좀더 두고 봐야 할 것 같다.



참고 자료

2013/04/12 02:25 2013/04/12 02:25