FE개발자의 성장 스토리 08 : WebAssembly 개발기

안녕하세요, FE플랫폼팀에서 전사 에디터 및 웹 사진 편집기를 개발하고 있는 sky입니다. 기존 사진 편집기 성능 개선을 위한 방법으로 Web Assembly를 조사하게 되었습니다. Web Assembly를 사용해 본 경험을 공유하고자 합니다.

들어가기에 앞서, 해당 글은 웹 어셈블리를 체험 및 소개하려는 글입니다. 자료조사, 개발을 통하여 얻은 경험을 공유하여 더 많은 개발자들이 쉽게 WebAssembly에 접근하고자 하는 작은 바람으로 쉽게 작성하였습니다.

 


 

WebAssembly란?

 

WebAssembly(이하 웹 어셈, WASM)는 2015년부터 개발된 기술로 2017년에 처음 발표되었습니다. 현재는 W3C WebAssembly Working group과 Community Group 서 웹 표준으로 개발 되고 있습니다.

IE11을 제외한 최신 브라우저에서 대부분 사용 가능하며 기존 JS로 개발되던 패러다임을 떠나, C, C++, Rust를 사용한 효과적이면서, 더 넓은 기능을 제공할 수 있도록 개발되고 있습니다.

 

WebAssembly 시작하기

 

WebAssembly를 사용하기 위해서는 Emscripten을 설치해야 합니다.

emscripten은 현재 npm에 배포되어 있지 않아. emscripten github에서 clone 받아 사용합니다.
공식 홈페이지에서 안내하는 설치 스크립트입니다.

window 사용자라면 source ./emsdk_env.sh을 emsdk_env.bat로 바꿔 사용해야 합니다. 

emscripten은 python script로 되어 있습니다. python 3.6 이상 버전부터 사용 가능합니다. OS에 따라 XCode cli, git, CMake 등 추가 설치가 필요합니다. 추가 설치 가이드

정상적으로 설치가 되었는지 확인을 위해 간단한 데모를 작성했습니다. 실행이 된다면 Hello world를 출력하는 main.cpp를 작성합니다.

이후 cpp를 컴파일 하기 위해 스크립트를 실행합니다.

em++ main.cpp -o index.html

설치 이후 터미널을 닫지 않고 스크립트를 실행하면 index.wasm, index.js, index.html 3개의 파일이 생성됩니다. 만약 터미널을 닫게 되면 em++: command not found 같은 에러를 만나게 됩니다.

아쉽게도 emscripten 환경설정 작업은 새로운 터미널이 실행될 때마다 필요합니다.
생성된 index.html 파일을 실행시키면,  

정상적으로 WASM이 불러와지고 실행이 됩니다. 생성된 wasm, html 파일의 목적은 각각 cpp를 컴파일한 결과와 데모 페이지입니다. 하지만 js 파일은 어떤 목적으로 생성이 되는 걸까요?

wasm 파일은 바로 읽어서 사용할 수 없습니다. 그래서 html과 wasm을 이어주기 위한 연결 다리가 필요합니다. 그 연결 다리 역할을 해주는 것이 js 파일이고, 이 파일을 glue(접착제) 코드라고 합니다.

 

Javascript vs WebAssembly

 

W3C 웹 어셈블리 그룹에서 웹 어셈블리의 목적을 아래와 같이 소개하고 있습니다.

  • 빠르고, 효율적이고, 높은 이식성을 가진다.
  • 읽기 쉬우면서 디버깅이 가능해야 한다.
  • 안전하고, 웹을 망치지 않아야 한다.

 

이번 경험기에서는 빠른 성능 목표에 대해서 다뤄보려고 합니다.

 

로딩

웹 어셈은 기존 js에 비해 파일을 읽는 로딩 시간이 빠릅니다. 먼저 js 파일은 텍스트로 되어있어 브라우저에서 모두 읽어야만 실행이 가능합니다. 읽어진 js 파일은 컴파일 이후 실행이 가능합니다.
반면 wasm 파일은 이미 컴파일이 되어있고, 이진 파일로 되어있어 브라우저가 더 읽기 쉬운 형태를 가집니다. wasm 파일도 실행을 위하여 컴파일 과정이 필요하지만 js보다 기계 친화적인 형태로 되어 있어 컴파일 시간이 짧습니다.

 

성능

효율성을 목적으로 설계가 된 만큼 성능 또한 빠릅니다.
실제로 실행 시간이 native code에 비해 20%밖에 느리지 않다는 결과가 있습니다. wasm이 샌드박스 환경과 보안을 위한 제약조건을 가진 상태에서 실행이 되는 것을 생각한다면 20%는 상당히 높은 성능을 가진다고 볼 수 있습니다.

 

성능 비교

실제로 성능이 얼마나 차이가 발생하는지 확인을 위하여 간단한 데모를 통하여 테스트를 진행하였습니다. 테스트는 이미지를 필터링하는 과정을 js와 wasm으로 구현하였으며 같은 시간 복잡도를 가지도록 로직을 구성하였습니다.

canvas를 사용하여 측정하였으며, canvas에서 imageData만 추출한 이후 필터링 과정만 성능 측정에 사용하였습니다.

 

비교 조건

  • 이미지 : 512 x 512, lena.jpg
  • 비교 방식 : 같은 필터링 과정을 50회 반복 후 평균 값 비교
  • 사용된 필터 : Gray scale, Threshold, Histogram equalize, Gaussian blur, Canny edge detection

각 이미지의 막대는 최대 런타임에 대한 현재 런타임 비율입니다.

 

50회 평균 실행 시간을 표로 정리하면 다음과 같습니다.

( 실행 시간, 단위: ms )

성능 비교 분석

결과

테스트에 사용한 5개 필터에 대해서 wasm으로 작성된 필터가 js에 비해 약 2 ~ 3배 빠른 성능을 나타냅니다.

 

원인

성능 차이가 발생하는 원인은 자바스크립트 엔진이 처리하는 과정에 있습니다. SpiderMonkey 엔진의 처리 과정과 비교하여 정리하였습니다.

각 막대의 길이는 참고용이며, 비교를 위한 표현입니다. 실제와는 차이가 있을 수 있습니다.

 

javascript 처리 과정

 

WebAssembly 처리 과정

 

각 과정을 비교하면 다음과 같습니다.

구문 해석 (parse – decode) :
js 파일이 브라우저에 전달되면, 추상 구문 트리 ( AST : Abstract Syntax Tree )로 변환이 되어야 합니다. AST는 각 엔진마다 다르지만 바이트 코드 형태로 변환이 됩니다. 반면, wasm 파일은 이미 바이트 코드 형태이기 때문에, 변환 과정이 필요 없이 해석(decode)만 하여 검증 과정만 거치면 됩니다.

 

컴파일 및 최적화 (compile + optimize) :
js는 각 브라우저마다 다르지만 컴파일 과정을 거칩니다. 기본 컴파일러나, JIT (Just-in-time) 컴파일러를 사용하는 방식입니다. 해당 과정은 여러 요인이 있지만 WASM은 LLVM 컴파일러를 사용하여 이미 많은 최적화가 진행된 상태입니다. 따라서 js에 비해 최적화 과정이 적어지게 됩니다.

 

재 최적화 (re-optimize) :
재 최적화는 js에서만 일어나는 과정입니다. 이는 JIT 컴파일러를 사용하는 과정에서 간혹 여러 이유로 인해 기존 최적화를 버리고 다시 최적화를 하는 과정입니다.
WASM은 명시적인 타입 같은 이유로, JIT에서 데이터를 수집, 타입 추론과 같은 과정이 필요 없습니다.

 

실행 (execute) :
js가 빠르게 실행되기 위해서는 JIT를 자세히 알아야 합니다. 하지만 이는 실제로 알기도 어렵고, 모든 브라우저에서 동일하게 적용되지는 않습니다. 반면 WASM은 이를 위한 노력이 필요 없습니다. 이미 최적화가 많이 진행되었기 때문이죠.
이는 js가 프로그래머를 위해 작성된 언어라면, wasm는 컴파일러를 위해 작성되었습니다. 결과적으로 더 이상적인 명령어 셋(instruction set)을 제공하여 10% ~ 800%의 성능 차이를 가져옵니다.

 

가비지 컬렉션 (GC : garbage collection) :
아직까지 WASM에서는 GC를 제공하지 않습니다.  C, C++처럼 메모리를 수동으로 관리하는 것이 어렵지만, 안정적인 성능을 제공할 수는 있습니다.

 

결과적으로 여러 이유를 통해 WASM이 js에 비해 좋은 성능을 가지게 됩니다. 몇몇 경우는 예외가 있을 수 있지만, 가까운 미래에 해결될 내용으로 보입니다. 개인적으로 사람 친화적인 js와 기계 친화적인 WASM이 가장 쉬운 설명 내용이었습니다.

 

WebAssembly의 미래를 위해서

 

이번 WebAssembly를 적용하면서 겪었던 WebAssembly의 주관적인 단점을 적어보고자 합니다.

 

추가적인 언어의 학습

WebAssembly를 사용하기 위해서는 js가 아닌 LLVM이 지원하는 C, C++, Rust 같은 언어로 작성을 해야 합니다. 이는 지원 언어를 학습한 적이 없다면, 사용을 위해 추가적으로 문법을 학습해야 합니다. 명시적인 타입이 없는 언어에서, 있는 언어를 학습한다는 것은 생각보다 쉬운 일은 아닐 것입니다.

 

제한된 지원 범위

아직까지 WebAssembly 도구인 Emscripten에서는 number, string, array 타입만 지원합니다. array 타입도 지원은 하지만 전달 간에는 포인터 형태로 변경해야 하기 때문에 사실상 number와 같습니다. 타입이 제한이 되어있기 때문에 활용 방안도 아직까지는 제한이 됩니다.

 

비교적 적은 참고 자료

WebAssembly를 체험하면서 가장 힘들었던 부분입니다. 발표된 지 올해로 4년 된 기술이지만 참고 자료가 적습니다. 개발 환경, 개발 언어가 여럿인 만큼 발생할 수 있는 에러 또한 넓은 범위에 걸쳐 나타납니다. 하지만 넓은 범위를 전부 덮을 만큼, 사용량이 많지 않아 참고 자료를 찾기 어렵습니다.

 

 

마치며

 

FE플랫폼팀에서는, WebAssembly를 이용하여 티스토리, 다음 카페에서 사용 중인 전사 에디터의 사진 편집기 성능 개선에 도전하고 있습니다.

현재 전사 에디터 사진 편집기는 대부분 필터링 서버에 요청을 통하여 이미지 처리를 진행합니다. 실제 이미지 처리를 진행하는 시간보다 더 많은 시간을 통신에 사용합니다. 이 과정을 웹 어셈으로 만든 필터를 제공해 통신에 걸리는 비용을 없애고 필터링 자체 성능을 올릴 수 있습니다.

성능 하나만 바라보며 정말 많은 시간을 투자해야 하는 기술이란 느낌을 많이 받았습니다. 아직까지는 활용 방안이 넓지 않아 메이저 기술로 인식되기까지는 시간이 필요한 기술로 느껴집니다. 이 글을 기점으로 웹 어셈블리를 한번 사용해보고 체험해 보시면서 활용 범위를 넓혀가는 건 어떨까요?

“카카오 FE플랫폼팀에서 함께 WASM을 딥드라이브 하실 여러분들을 찾고 있습니다.”

👉 FE플랫폼팀에 합류하기

 


 

참고 

Latest Posts