WebAssembly 모듈의 효율적인 로드

WebAssembly를 사용할 때 모듈을 다운로드하고 컴파일하고 인스턴스화한 다음 JavaScript에서 내보내는 내용을 사용하려는 경우가 많습니다. 이 게시물에서는 최적의 효율성을 위해 권장되는 접근 방식에 대해 설명합니다.

WebAssembly로 작업할 때 모듈을 다운로드하고, 컴파일하고, 인스턴스화하고, JavaScript로 내보낸 모든 것을 사용할 수 있습니다. 이 게시물은 일반적이지만 최적화되지 않은 코드로 시작합니다. 하고, 가능한 몇 가지 최적화에 대해 설명하고, 최종적으로는 JavaScript에서 WebAssembly를 실행하는 가장 간단하고 효율적인 방법입니다.

다음 코드 스니펫은 최적화되지 않는 방식이지만 전체 다운로드-컴파일-인스턴스화 댄스를 수행합니다.

사용하지 마세요.

(async () => {
  const response = await fetch('fibonacci.wasm');
  const buffer = await response.arrayBuffer();
  const module = new WebAssembly.Module(buffer);
  const instance = new WebAssembly.Instance(module);
  const result = instance.exports.fibonacci(42);
  console.log(result);
})();

new WebAssembly.Module(buffer)를 사용하여 응답 버퍼를 모듈로 변환하는 방법을 확인하세요. 이것은 동기 API를 사용합니다. 즉, 완료될 때까지 기본 스레드를 차단합니다. 사용을 막기 위해 Chrome은 4KB를 초과하는 버퍼에서는 WebAssembly.Module를 사용 중지합니다. 크기 제한을 우회하려면 대신 await WebAssembly.compile(buffer)를 사용하세요.

(async () => {
  const response = await fetch('fibonacci.wasm');
  const buffer = await response.arrayBuffer();
  const module = await WebAssembly.compile(buffer);
  const instance = new WebAssembly.Instance(module);
  const result = instance.exports.fibonacci(42);
  console.log(result);
})();

await WebAssembly.compile(buffer)여전히 최적의 접근 방식은 아니지만 둘째,

수정된 스니펫의 거의 모든 작업이 이제 비동기식입니다. await를 사용하면 명확합니다. 유일한 예외는 동일한 4KB 버퍼를 갖는 new WebAssembly.Instance(module)입니다. Chrome의 크기 제한. 일관성 및 기본 스레드 유지를 위해 무료인 경우 비동기식으로 WebAssembly.instantiate(module)입니다.

(async () => {
  const response = await fetch('fibonacci.wasm');
  const buffer = await response.arrayBuffer();
  const module = await WebAssembly.compile(buffer);
  const instance = await WebAssembly.instantiate(module);
  const result = instance.exports.fibonacci(42);
  console.log(result);
})();

앞서 힌트를 드린 compile 최적화로 돌아가 보겠습니다. 스트리밍 compile을 사용하는 경우 브라우저는 이미 모듈 바이트가 아직 다운로드되고 있는 동안 WebAssembly 모듈을 컴파일하기 시작합니다. 다운로드 이후 컴파일은 병렬로 이루어지며 특히 대용량 페이로드의 경우 속도가 더 빠릅니다.

다운로드 시간이
시간이 WebAssembly 모듈의 컴파일 시간보다 길면 WebAssembly.compileStreaming()이
마지막 바이트가 다운로드된 직후 컴파일을 완료합니다.

이 최적화를 사용 설정하려면 WebAssembly.compile 대신 WebAssembly.compileStreaming를 사용하세요. 이 변경을 통해 중간 배열 버퍼를 제거할 수도 있습니다. 이제 await fetch(url)에서 직접 반환한 Response 인스턴스

(async () => {
  const response = await fetch('fibonacci.wasm');
  const module = await WebAssembly.compileStreaming(response);
  const instance = await WebAssembly.instantiate(module);
  const result = instance.exports.fibonacci(42);
  console.log(result);
})();
드림

WebAssembly.compileStreaming API는 Response로 확인되는 프로미스도 허용합니다. 인스턴스를 만들 수 있습니다 코드의 다른 위치에 response가 필요하지 않은 경우 프로미스를 전달할 수 있습니다. 결과를 명시적으로 await하지 않고 fetch에서 직접 반환합니다.

(async () => {
  const fetchPromise = fetch('fibonacci.wasm');
  const module = await WebAssembly.compileStreaming(fetchPromise);
  const instance = await WebAssembly.instantiate(module);
  const result = instance.exports.fibonacci(42);
  console.log(result);
})();

다른 곳에서도 fetch 결과가 필요하지 않다면 다음과 같이 직접 전달할 수도 있습니다.

(async () => {
  const module = await WebAssembly.compileStreaming(
    fetch('fibonacci.wasm'));
  const instance = await WebAssembly.instantiate(module);
  const result = instance.exports.fibonacci(42);
  console.log(result);
})();

개인적으로 별도의 줄에 두는 것이 더 읽기 쉽다고 생각합니다.

응답을 모듈로 컴파일한 다음 즉시 인스턴스화하는 방법을 알아보세요. 알고 보니 WebAssembly.instantiate는 한 번에 컴파일하고 인스턴스화할 수 있습니다. 이 WebAssembly.instantiateStreaming API는 스트리밍 방식으로 이 작업을 실행합니다.

(async () => {
  const fetchPromise = fetch('fibonacci.wasm');
  const { module, instance } = await WebAssembly.instantiateStreaming(fetchPromise);
  // To create a new instance later:
  const otherInstance = await WebAssembly.instantiate(module);
  const result = instance.exports.fibonacci(42);
  console.log(result);
})();

인스턴스가 하나만 필요하면 module 객체를 유지하는 것은 의미가 없습니다. 코드를 더욱 단순화합니다.

// This is our recommended way of loading WebAssembly.
(async () => {
  const fetchPromise = fetch('fibonacci.wasm');
  const { instance } = await WebAssembly.instantiateStreaming(fetchPromise);
  const result = instance.exports.fibonacci(42);
  console.log(result);
})();

우리가 적용한 최적화는 다음과 같이 요약할 수 있습니다.

  • 비동기 API를 사용하여 기본 스레드 차단 방지
  • 스트리밍 API를 사용하여 WebAssembly 모듈을 더 빠르게 컴파일하고 인스턴스화
  • 필요하지 않은 코드를 작성하지 마세요.

WebAssembly를 사용해 보세요.