WebAssembly (WASM) for High Performance Apps · 강의

완전한 WASM 애플리케이션 구축

지금까지 습득한 모든 지식을 적용하여 WebAssembly로 구동되는 정교한 고성능 애플리케이션을 설계하고 구축합니다.

레슨 3/412개 단계

완전한 WASM 애플리케이션 구축은(는) CoddyKit의 무료 WebAssembly (WASM) for High Performance Apps 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 WebAssembly (WASM) for High Performance Apps 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. WebAssembly (WASM) for High Performance Apps 강의에는 총 4개의 강의가 포함되어 있습니다.

이 강의의 일부는 아직 번역되지 않았으며 영어로 표시됩니다.

Building a Complete WASM App

You've learned the building blocks of WebAssembly. Now, let's bring it all together! A 'complete' WASM application integrates high-performance WASM code with a responsive JavaScript host, managing data, concurrency, and error handling effectively.

We'll explore how to architect such an application by applying what you've learned.

WASM App Blueprint

A typical high-performance WASM application involves several layers. The JavaScript host handles UI and high-level logic, while WASM modules perform intensive computation. For complex tasks, Web Workers can offload WASM execution to separate threads, preventing UI freezes. This layered approach ensures both speed and responsiveness.

  • JavaScript Host: UI, API calls, orchestrates WASM.
  • WASM Module(s): Core high-performance logic.
  • Web Workers: Background execution for non-blocking operations.
  • Shared Memory: Efficient data exchange between threads.

Setting Up Our Rust-WASM Project

For building a complete application, our Rust WASM project needs to be set up to compile efficiently and interact smoothly with JavaScript. We use wasm-pack to build our Rust code into a WASM module and wasm-bindgen to generate the necessary JavaScript glue code for seamless interoperability.

High-Performance Image Logic

Let's consider a simple image processing task: converting an image to grayscale. The computationally intensive part will live in our Rust WASM module. We'll write a function that takes a pointer to image data in WASM's linear memory and processes it directly.

Try running this simplified Rust example:

pub extern "C" fn apply_grayscale(ptr: *mut u8, len: usize) {
    let slice = unsafe {
        assert!(!ptr.is_null());
        std::slice::from_raw_parts_mut(ptr, len)
    };

    // Image data is typically RGBA, 4 bytes per pixel
    for i in (0..len).step_by(4) {
        let r = slice[i] as u32;
        let g = slice[i + 1] as u32;
        let b = slice[i + 2] as u32;
        let gray = ((r + g + b) / 3) as u8;

        slice[i] = gray;
        slice[i + 1] = gray;
        slice[i + 2] = gray;
        // Alpha channel (slice[i+3]) remains unchanged
    }
}

// A minimal main function for runnable context.
// In WASM, `apply_grayscale` would be directly called by JS.
fn main() {
    let mut image_data = [
        255, 0, 0, 255,   // Red pixel
        0, 255, 0, 255    // Green pixel
    ];
    let ptr = image_data.as_mut_ptr();
    let len = image_data.len();
    apply_grayscale(ptr, len);
    println!("Processed first pixel R: {} G: {} B: {}", image_data[0], image_data[1], image_data[2]);
}

JS to WASM: Image Data Input

To pass our image data (e.g., from a canvas ImageData) to the WASM module, JavaScript needs to write it into WASM's linear memory. This involves getting a pointer from WASM to a memory region, then copying the Uint8ClampedArray into it. wasm-bindgen often simplifies this, but direct memory access is key.

Try running this example:

// Helper to simulate WASM memory and function call
const wasmMemory = new WebAssembly.Memory({ initial: 1 }); // 1 page = 64KB
const wasmByteView = new Uint8ClampedArray(wasmMemory.buffer);

// Simulate the WASM function (simplified grayscale logic)
const mockApplyGrayscale = (ptr, len) => {
  for (let i = ptr; i < ptr + len; i += 4) {
    const r = wasmByteView[i];
    const g = wasmByteView[i + 1];
    const b = wasmByteView[i + 2];
    const gray = Math.floor((r + g + b) / 3);
    wasmByteView[i] = gray;
    wasmByteView[i + 1] = gray;
    wasmByteView[i + 2] = gray;
  }
};

// Our "wasmModule" for this example
const wasmModule = {
  apply_grayscale: mockApplyGrayscale,
  memory: wasmMemory
};

// Example imageData (RGBA, 2 pixels)
const originalImageData = new Uint8ClampedArray([
  200, 100, 50, 255, // Pixel 1: Orange-ish
  50, 150, 200, 255  // Pixel 2: Blue-ish
]);

// Copy original data to WASM memory (offset 0 for simplicity)
wasmByteView.set(originalImageData, 0);

console.log("Original data in WASM memory (first 8 bytes):");
console.log(Array.from(wasmByteView.slice(0, 8)));

// Call the "WASM" function
wasmModule.apply_grayscale(0, originalImageData.length);

console.log("Processed data in WASM memory (first 8 bytes):");
console.log(Array.from(wasmByteView.slice(0, 8)));

WASM to JS: Processed Output

After WASM processes the data, the results are already present in its linear memory. JavaScript can then read this modified data directly from the WebAssembly.Memory buffer. This avoids costly data copying, especially for large datasets like image buffers.

Try running this example:

// Continuing from the previous example where wasmByteView has processed data

// Helper to simulate WASM memory and function call
const wasmMemory = new WebAssembly.Memory({ initial: 1 }); // 1 page = 64KB
const wasmByteView = new Uint8ClampedArray(wasmMemory.buffer);

// Simulate the WASM function (simplified grayscale logic)
const mockApplyGrayscale = (ptr, len) => {
  for (let i = ptr; i < ptr + len; i += 4) {
    const r = wasmByteView[i];
    const g = wasmByteView[i + 1];
    const b = wasmByteView[i + 2];
    const gray = Math.floor((r + g + b) / 3);
    wasmByteView[i] = gray;
    wasmByteView[i + 1] = gray;
    wasmByteView[i + 2] = gray;
  }
};

// Our "wasmModule" for this example
const wasmModule = {
  apply_grayscale: mockApplyGrayscale,
  memory: wasmMemory
};

// Example imageData (RGBA, 2 pixels)
const originalImageData = new Uint8ClampedArray([
  200, 100, 50, 255, // Pixel 1: Orange-ish
  50, 150, 200, 255  // Pixel 2: Blue-ish
]);

// Copy original data to WASM memory (offset 0 for simplicity)
wasmByteView.set(originalImageData, 0);

// Call the "WASM" function
wasmModule.apply_grayscale(0, originalImageData.length);

// To get the processed image data back into a JS array:
const processedImageData = new Uint8ClampedArray(
  wasmByteView.slice(0, originalImageData.length)
);

console.log("Data read back into JS array (first 8 bytes):");
console.log(Array.from(processedImageData.slice(0, 8)));

Keeping UI Responsive with Workers

For heavy computations like image processing, running WASM directly on the main thread can block the UI. The solution is to offload these tasks to a Web Worker. The worker loads the WASM module and performs the computation, communicating results back to the main thread via messages.

This example shows the message passing concept:

// main.js (simulated)
const worker = {
  onmessage: null,
  postMessage: (msg, transfers) => {
    console.log("Main thread sends to worker:", msg.type);
    // Simulate worker receiving and responding
    setTimeout(() => {
      if (msg.type === 'processImage') {
        const imageData = new Uint8ClampedArray(msg.data.buffer);
        // Simulate WASM processing
        for (let i = 0; i < imageData.length; i += 4) {
          const r = imageData[i];
          const g = imageData[i + 1];
          const b = imageData[i + 2];
          const gray = Math.floor((r + g + b) / 3);
          imageData[i] = gray;
          imageData[i + 1] = gray;
          imageData[i + 2] = gray;
        }
        if (worker.onmessage) {
          worker.onmessage({ data: { type: 'imageProcessed', result: imageData }, transfers: [imageData.buffer] });
        }
      }
    }, 10);
  }
};

worker.onmessage = (event) => {
  if (event.data.type === 'imageProcessed') {
    console.log("Main thread receives from worker:", event.data.type);
    console.log("Processed image data (first 8 bytes):", Array.from(event.data.result.slice(0, 8)));
    // Update UI with processed image data
  }
};

function sendImageToWorker(imageData) {
  // Transferrable objects improve performance for large data
  worker.postMessage({ type: 'processImage', data: imageData }, [imageData.buffer]);
}

// Example call (in main.js context, after image loaded)
const demoImageData = new Uint8ClampedArray([200,100,50,255, 50,150,200,255]);
console.log("Original demo image data (first 8 bytes):", Array.from(demoImageData.slice(0, 8)));
sendImageToWorker(demoImageData);

Shared Memory for Concurrency

While Web Workers prevent UI blocking, transferring large amounts of data between the main thread and workers can still be a bottleneck. SharedArrayBuffer allows both threads to access the same block of memory simultaneously. This is crucial for truly parallel WASM computations that need to coordinate or share state.

  • One memory block: Accessible by main thread and all workers.
  • No copying: Eliminates data transfer overhead.
  • Atomics: Required for safe, synchronized access to shared memory.
  • Use case: Real-time simulations, complex multi-threaded algorithms.

Handling Errors Gracefully

In a complete application, robust error handling is vital. When an error occurs in WASM, it typically crashes the module. We need mechanisms to catch these and communicate them back to JavaScript. Strategies include returning specific error codes, exporting a WASM function to set an error state, or using wasm-bindgen's built-in error propagation for Rust Result types.

  • Return Codes: WASM function returns 0 for success, non-zero for error.
  • Error State: WASM exports a function to retrieve last error message.
  • wasm-bindgen Errors: Rust Result types can be automatically converted to JS exceptions.

Orchestrating the Full Workflow

Let's put all the pieces together for our grayscale application. Imagine a web page with an image:

  1. User uploads an image.
  2. JavaScript reads the image into an ImageData object.
  3. JS sends the ImageData (or its underlying Uint8ClampedArray) to a Web Worker.
  4. The Web Worker loads the WASM module.
  5. The Worker calls the WASM grayscale function, passing the image data pointer.
  6. WASM processes the image data in memory.
  7. The Worker receives the processed data (already in its memory view).
  8. The Worker sends the processed data back to the main thread.
  9. The main thread updates the canvas with the new ImageData.

This full cycle demonstrates a high-performance, non-blocking WASM application.

Integrated WASM Concepts

Consider a complex WebAssembly application that performs real-time video processing. It uses Rust compiled to WASM, interacts with JavaScript, and needs to maintain a responsive user interface. Which of the following strategies are crucial for building such an application effectively?

Building Robust WASM Apps

You've now seen how to bring together various WebAssembly concepts to build a sophisticated application. From architecting the interaction between JavaScript and WASM, to managing memory, leveraging Web Workers for concurrency, and ensuring robust error handling, these principles are key to developing high-performance, production-ready WASM solutions.

The future of WASM is about seamless integration and unlocking new levels of web application capability.

무료로 시작

AI 튜터와 함께 WebAssembly (WASM) for High Performance Apps을(를) 배우세요 — 무료

브라우저에서 실제 코드를 작성하고 실행하며, 24/7 AI 튜터로부터 즉각적인 도움을 받고, 웹이나 앱에서 중단한 부분부터 계속 학습하세요.

코스
12
레슨
48

자주 묻는 질문

“완전한 WASM 애플리케이션 구축” 강의는 무료인가요?

네 — “완전한 WASM 애플리케이션 구축” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 WebAssembly (WASM) for High Performance Apps 강의 전체를 잠금 해제할 수 있습니다. WebAssembly (WASM) for High Performance Apps 강의에는 총 4개의 강의가 포함되어 있습니다.

“완전한 WASM 애플리케이션 구축”에서 뭘 배우나요?

지금까지 습득한 모든 지식을 적용하여 WebAssembly로 구동되는 정교한 고성능 애플리케이션을 설계하고 구축합니다. 브라우저에서 직접 실행하는 실습 코드로 WebAssembly (WASM) for High Performance Apps을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

WebAssembly (WASM) for High Performance Apps을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 WebAssembly (WASM) for High Performance Apps은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.

“완전한 WASM 애플리케이션 구축” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 WebAssembly (WASM) for High Performance Apps 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 WebAssembly (WASM) for High Performance Apps 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. WASM 컴포넌트 모델 및 향후 API
  2. 고급 WASM 도구 및 생태계
  3. 완전한 WASM 애플리케이션 구축
  4. WASM 가비지 컬렉션과 참조 형식
← WebAssembly (WASM) for High Performance Apps(으)로 돌아가기