WebAssembly 사용하기

youseock·2024년 2월 29일

[JS] WebAssembly

목록 보기
2/4
post-thumbnail

C 언어를 컴파일 후 직접 실행 해보고, emsdk를 이용하여 wasm으로 컴파일 한 후 브라우저에서 사용하는 내용을 담았습니다.

C 언어 컴파일 후 실행해보기

  • C 언어를 처음 사용해서 gpt의 도움을 받았습니다

C 언어 작성하기

shuffle.c

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

void shuffle(int arr[], int n) {
   srand(time(NULL));
   for (int i = n - 1; i > 0; i--) {
       int j = rand() % (i + 1);
       int temp = arr[i];
       arr[i] = arr[j];
       arr[j] = temp;
   }
}

int main(int argc, char *argv[]) {
   int n = argc - 1; // 명령행 인수의 개수를 배열의 길이로 사용
   int arr[n]; // 배열 생성

   // 명령행 인수를 배열로 변환
   for (int i = 0; i < n; i++) {
       arr[i] = atoi(argv[i + 1]);
   }

   shuffle(arr, n);

   printf("Shuffled array: ");
   for (int i = 0; i < n; i++) {
       printf("%d ", arr[i]);
   }
   printf("\n");

   return 0;
}

C 언어 컴파일하기

gcc -o shuffle shuffle.c
  • gcc : GNU Complier Collection을 사용하겠다.
  • -o : 출력 파일의 이름을 -o 뒤에 오는 이름으로 하겠다.

컴파일된 파일 실행하기

./shuffle 1 2 3 4 5
#output 2 4 5 3 1

C 언어를 Emscripten으로 컴파일하기

💡 Emscripten는 웹 플랫폼에 중점을 둔 WASM 컴파일러 툴 체인이다.

Emscripten 환경 준비하기 (SDK 설치)

💡 SDK는 특정한 소프트웨어를 개발하기 위해 필요한 도구, 라이브러리, 문서 등을 모아놓은 패키지를 뜻한다.

core Emscripten SDK 다운받기

git clone https://github.com/emscripten-core/emsdk.git
cd emsdk

emsdk 최신화한 후 활성 상태로 만들기

git pull

# 최신 버전 다운받기
./emsdk install latest

# 최신 버전 활성화
./emsdk activate latest

# 환경변수 설정
source ./emsdk_env.sh
  • global로 Emscripten SDK 사용하기

    💡 ./emsdk activate latest를 입력하면 아래와 같은 결과를 받을 수 있다.ehco 부분을 그대로 복붙하면 어느 경로에서든 터미널로 Emscripten SDK에 접근할 수 있다.

emsdk 설치 확인

emsdk list
  • 명령어를 찾을 수 없다는 결과가 나온다면, emsdk 폴더에서 다시 ./emsdk activate latest를 수행한 후에 환경변수 설정에 이상이 없는 지 체크하자.

emsdk로 컴파일할 C 코드 작성

hello.c

#include <stdio.h>
#include <emscripten/emscripten.h>

int main() {
   return 0;
}

EMSCRIPTEN_KEEPALIVE int throw_number(int argc, char ** argv) {
    return 777;
}
  • emsdk를 이용한 컴파일 과정에서 사용하지 않는 함수들은 제거되는데, 이를 방지하기 위해 emscripten.h 라이브러리의 EMSCRIPTEN_KEEPALIVE를 가져와 모듈로 사용할 함수 앞에 붙여주자.
  • 컴파일러의 입장에서 emscripten.h의 경로를 찾을 수 없기 때문에 아래와 같은 에러가 발생한다.
    이를 해결하기 위해 which emcc로 c 언어를 컴파일 해줄 emscripten 컴파일러의 위치를 찾자
    c_cpp_properties.json 파일에 includePath에 추가하기
    • includePath는 컴파일러가 헤더 파일을 찾을 위치를 지정할 때 사용된다.
    1. 프로젝트의 루트 디렉토리에 .vscode를 찾고, 없다면 만들자
    2. .vscode 내에 c_cpp_properties.json 파일을 찾고, 없다면 만들자
    3. which emcc를 통해 얻은 path를 c_cpp_properties.json에 추가하자
      {
        "configurations": [
        { 
          // ...
          "includePath": [
            "${workspaceFolder}/**",
            "/Users/jeong-youseock/Desktop/emsdk/upstream/emscripten/**" 👈
          ]
        }
        ]
      }

C 코드 emsdk을 이용하여 Wasm으로 컴파일하기

emcc --no-entry ./throw_number.c -o ./throw_number.wasm \
    -O3 \
    -s ENVIRONMENT='web' \
    -s EXPORTED_FUNCTIONS='["_throw_number","_malloc","_free"]'

하나씩 살펴보자

  • emcc : Emscripten 컴파일러로 C/ C++ 코드를 WASM으로 변환해 줘!
  • —no-entry : C 코드가 메인 함수를 가지고 있지 않고, 다른 곳에서 호출될 모듈만 포함하고 있어!
  • ./throw_number.c : 해당 파일을 WASM으로 바꿔줘
  • -o ./throw_number.wasm : 컴파일 후 throw_number.wasm 이름으로 저장해 줘!
  • -03 : 가장 높은 수준으로 최적화 부탁해!
  • -s ENVIRONMENT='web' : 웹 플랫폼에서 실행될 거야!
  • -s EXPORTED_FUNCTIONS='["_throw_number","_malloc","_free"]'
    • "_throw_number","_malloc","_free" 라는 이름의 함수를 export 할거야!

브라우저에서 Wasm 파일 사용하기

WebAssembly 객체란

WebAssembly 객체는 Wasm과 관련된 모든 기능에 대한 네임스페이스 역할을 한다. 즉, Wasm 모듈과 상호작용하고 제어하는 데 사용되는 메서드와 속성을 제공하며, WebAssembly 객체를 사용하여 Wasm 모듈을 로드하고 실행하며, Wasm 모듈을 호출하고 결과를 처리할 수 있다.

💡 네임스페이스 역할을 한다는 것은 무엇을 의미할까?

일반적인 내장 객체는 생성자 역할을 하지만 그렇지 않은 내장 객체들도 있다.
WebAssembly도 Intl, Math와 같이 비슷한 역할을 하는 메소드들의 패키지이다.

WebAssembly 객체의 주요 기능

  • Wasm 모듈 로드
    • WebAssembly.instantiateStreaming()나 WebAssembly.instantiate() 메소드를 사용하여 Wasm 모듈을 로드하고 인스턴스화할 수 있다.
  • Wasm 모듈 호출
    • 로드된 Wasm 모듈을 호출하고 결과값을 반환받을 수 있다.
  • 메모리 엑세스
    • Wasm 모듈의 메모리에 접근하여 데이터를 읽거나 쓸 수 있다.
  • 외부 함수 등록
    • 자바스크립트 함수를 Wasm 모듈에 등록하여 Wasm 코드에서 호출할 수 있다.
    • 즉, Wasm 모듈과 자바스크립트 모듈이 상호작용하게 할 수 있다.
  • 커스텀 타입 정의
    • Wasm 모듈에서 사용되는 사용자 정의 타입을 정의하고 자바스크립트에서 처리할 수 있다.

WebAssembly로 Wasm을 동작 시켜보자

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <script>
    document.addEventListener("DOMContentLoaded", () => {
      const button = document.querySelector("button");
      const span = document.querySelector("span");
      button.addEventListener("click", () => {
        WebAssembly.instantiateStreaming(fetch("./throw_number.wasm"))
          .then((result) => {
            const { throw_number } = result.instance.exports;
            span.textContent = throw_number();
          })
          .catch(console.error);
      });
    });
  </script>
  <body>
    <button>어셈블 불러보기</button>
    <span></span>
  </body>
</html>
  • C 모듈을 로드한 후 호출해 결과 값을 span에 text로 추가하는 것을 볼 수 있다.
profile
자바스크립트 애호가

0개의 댓글