WebAssembly (WASM) Высокопроизводительные Вычисления

WebAssembly для высокопроизводительных вычислений в браузере с производительностью близкой к нативной

💻 Базовая Интеграция WebAssembly javascript

🟢 simple ⭐⭐

Загрузка и использование модулей WebAssembly в JavaScript

⏱️ 15 min 🏷️ wasm, webassembly, performance, integration
Prerequisites: JavaScript, WebAssembly basics
// Basic WebAssembly Integration
// C code (compiled to WASM):
/*
int add(int a, int b) {
    return a + b;
}

int calculate(int* numbers, int length) {
    int sum = 0;
    for (int i = 0; i < length; i++) {
        sum += numbers[i];
    }
    return sum;
}
*/

// JavaScript integration
async function loadWasm() {
  const response = await fetch('math.wasm');
  const buffer = await response.arrayBuffer();
  const wasmModule = await WebAssembly.compile(buffer);
  const wasmInstance = await WebAssembly.instantiate(wasmModule);

  return wasmInstance.exports;
}

// Usage
async function main() {
  const wasm = await loadWasm();

  // Simple addition
  const result = wasm.add(5, 3);
  console.log('5 + 3 =', result); // 8

  // Array operations
  const numbers = new Int32Array([1, 2, 3, 4, 5]);
  const pointer = wasm.allocateArray(numbers.length, 4);

  // Copy data to WASM memory
  const wasmMemory = new Uint8Array(wasm.memory.buffer);
  const dataView = new DataView(wasm.memory.buffer);
  for (let i = 0; i < numbers.length; i++) {
    dataView.setInt32(pointer + (i * 4), numbers[i], true);
  }

  const sum = wasm.calculate(pointer, numbers.length);
  console.log('Sum:', sum); // 15

  // Clean up
  wasm.deallocateArray(pointer, numbers.length);
}

main();

💻 Обработка Изображений WebAssembly javascript

🟡 intermediate ⭐⭐⭐

Высокопроизводительная обработка изображений с WASM

⏱️ 20 min 🏷️ wasm, image processing, graphics, performance
Prerequisites: JavaScript, Canvas API, WebAssembly
// WebAssembly Image Processing
// C++ code compiled to WASM for image manipulation
const wasmCode = `
(module
  (memory (export "memory") 1)
  (func (export "grayscale") (param $offset i32) (param $size i32)
    (local $i i32) (local $pixel i32) (local $gray i32)
    (loop $process
      (set_local $pixel
        (i32.load (get_local $offset))
      )
      (set_local $gray
        (i32.div_u
          (i32.add
            (i32.add
              (i32.and (get_local $pixel) 0xFF)
              (i32.shr_u (get_local $pixel) 8)
            )
            (i32.shr_u (get_local $pixel) 16)
          )
          3
        )
      )
      (i32.store (get_local $offset)
        (i32.or
          (i32.or
            (get_local $gray)
            (i32.shl (get_local $gray) 8)
          )
          (i32.shl (get_local $gray) 16)
        )
      )
      (set_local $offset
        (i32.add (get_local $offset) 4)
      )
      (br_if $process
        (i32.gt_u (get_local $size)
          (tee_local $i (i32.add (get_local $i) 1))
        )
      )
    )
  )
)
`;

// JavaScript image processor
class WasmImageProcessor {
  constructor() {
    this.wasmModule = null;
    this.wasmInstance = null;
  }

  async init() {
    const buffer = Uint8Array.from(atob(wasmCode), c => c.charCodeAt(0));
    this.wasmModule = await WebAssembly.compile(buffer);
    this.wasmInstance = await WebAssembly.instantiate(this.wasmModule);
  }

  async convertToGrayscale(imageData) {
    if (!this.wasmInstance) await this.init();

    const pixels = new Uint8ClampedArray(imageData.data);
    const wasmMemory = new Uint8Array(this.wasmInstance.exports.memory.buffer);

    // Copy image data to WASM memory
    wasmMemory.set(pixels);

    // Process in WASM
    this.wasmInstance.exports.grayscale(0, pixels.length / 4);

    // Copy processed data back
    const processedData = new Uint8ClampedArray(wasmMemory.slice(0, pixels.length));

    return new ImageData(processedData, imageData.width, imageData.height);
  }
}

// Usage
async function processImage() {
  const processor = new WasmImageProcessor();
  await processor.init();

  const canvas = document.getElementById('canvas');
  const ctx = canvas.getContext('2d');

  // Load and process image
  const img = new Image();
  img.onload = async () => {
    canvas.width = img.width;
    canvas.height = img.height;
    ctx.drawImage(img, 0, 0);

    const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
    const grayscale = await processor.convertToGrayscale(imageData);
    ctx.putImageData(grayscale, 0, 0);
  };
  img.src = 'image.jpg';
}

💻 WebAssembly в Web Workers javascript

🟡 intermediate ⭐⭐⭐⭐

Выполнение вычислений WASM в фоновых потоках

⏱️ 25 min 🏷️ wasm, workers, multithreading, performance
Prerequisites: JavaScript, Web Workers, WebAssembly
// WASM Web Worker for heavy computations
// worker.js
const wasmCode = `
(module
  (memory (export "memory") 1)
  (func (export "fibonacci") (param $n i32) (result i32)
    (local $a i32) (local $b i32) (local $temp i32) (local $i i32)
    (if (i32.le_s (get_local $n) 1)
      (then (return (get_local $n)))
    )
    (set_local $a (i32.const 0))
    (set_local $b (i32.const 1))
    (loop $fib
      (set_local $temp (get_local $b))
      (set_local $b (i32.add (get_local $a) (get_local $b)))
      (set_local $a (get_local $temp))
      (br_if $fib
        (i32.lt_s
          (tee_local $i (i32.add (get_local $i) 1))
          (get_local $n)
        )
      )
    )
    (return (get_local $b))
  )
)
`;

// Initialize WASM in worker
self.addEventListener('message', async (e) => {
  const { type, data } = e.data;

  if (type === 'init') {
    const buffer = Uint8Array.from(atob(wasmCode), c => c.charCodeAt(0));
    const wasmModule = await WebAssembly.compile(buffer);
    const wasmInstance = await WebAssembly.instantiate(wasmModule);
    self.wasm = wasmInstance.exports;
    self.postMessage({ type: 'ready' });
  }

  if (type === 'calculate') {
    const { n } = data;
    const result = self.wasm.fibonacci(n);
    self.postMessage({
      type: 'result',
      data: { n, result }
    });
  }
});

// main.js
class WasmWorkerPool {
  constructor(workerCount = 4) {
    this.workers = [];
    this.taskQueue = [];
    this.busyWorkers = new Set();

    // Initialize workers
    for (let i = 0; i < workerCount; i++) {
      this.createWorker();
    }
  }

  createWorker() {
    const worker = new Worker('worker.js');
    worker.addEventListener('message', (e) => {
      if (e.data.type === 'ready') {
        this.processQueue();
      } else if (e.data.type === 'result') {
        this.onWorkerComplete(worker, e.data);
      }
    });

    worker.postMessage({ type: 'init' });
    this.workers.push(worker);
  }

  calculate(n) {
    return new Promise((resolve) => {
      this.taskQueue.push({ n, resolve });
      this.processQueue();
    });
  }

  processQueue() {
    if (this.taskQueue.length === 0) return;

    const availableWorker = this.workers.find(w => !this.busyWorkers.has(w));
    if (!availableWorker) return;

    const task = this.taskQueue.shift();
    this.busyWorkers.add(availableWorker);

    availableWorker.postMessage({
      type: 'calculate',
      data: task
    });
  }

  onWorkerComplete(worker, result) {
    this.busyWorkers.delete(worker);
    const task = this.taskQueue.find(t => t.n === result.data.n);
    if (task) {
      task.resolve(result.data.result);
    }
    this.processQueue();
  }
}

// Usage
async function batchCalculate() {
  const pool = new WasmWorkerPool(4);
  const numbers = [30, 35, 40, 45, 50, 55];

  const promises = numbers.map(n => pool.calculate(n));
  const results = await Promise.all(promises);

  console.log('Fibonacci results:', results);
}

batchCalculate();

💻 Интеграция Rust WebAssembly rust

🔴 complex ⭐⭐⭐⭐

Компиляция Rust в WebAssembly для максимальной производительности

⏱️ 30 min 🏷️ rust, wasm, performance, matrix operations
Prerequisites: Rust, WebAssembly, wasm-bindgen
// Rust code for WebAssembly (src/lib.rs)
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
extern "C" {
    fn alert(s: &str);
}

#[wasm_bindgen]
pub fn greet(name: &str) {
    alert(&format!("Hello, {}!", name));
}

// High-performance matrix operations
#[wasm_bindgen]
pub struct Matrix {
    data: Vec<f32>,
    rows: usize,
    cols: usize,
}

#[wasm_bindgen]
impl Matrix {
    #[wasm_bindgen(constructor)]
    pub fn new(rows: usize, cols: usize) -> Matrix {
        Matrix {
            data: vec![0.0; rows * cols],
            rows,
            cols,
        }
    }

    #[wasm_bindgen]
    pub fn get(&self, row: usize, col: usize) -> f32 {
        self.data[row * self.cols + col]
    }

    #[wasm_bindgen]
    pub fn set(&mut self, row: usize, col: usize, value: f32) {
        self.data[row * self.cols + col] = value;
    }

    #[wasm_bindgen]
    pub fn multiply(&self, other: &Matrix) -> Matrix {
        assert_eq!(self.cols, other.rows);

        let mut result = Matrix::new(self.rows, other.cols);

        for i in 0..self.rows {
            for j in 0..other.cols {
                let mut sum = 0.0;
                for k in 0..self.cols {
                    sum += self.get(i, k) * other.get(k, j);
                }
                result.set(i, j, sum);
            }
        }

        result
    }

    #[wasm_bindgen]
    pub fn scale(&mut self, factor: f32) {
        for value in &mut self.data {
            *value *= factor;
        }
    }
}

// Performance benchmark
#[wasm_bindgen]
pub fn benchmark_fibonacci(n: u32) -> u64 {
    if n <= 1 {
        return n as u64;
    }

    let mut a = 0u64;
    let mut b = 1u64;

    for _ in 2..=n {
        let temp = a + b;
        a = b;
        b = temp;
    }

    b
}

// JavaScript integration
/*
// Import compiled WASM
import init, { Matrix, benchmark_fibonacci, greet } from './pkg/your_package.js';

async function main() {
    await init();

    // Matrix operations
    const m1 = new Matrix(1000, 1000);
    const m2 = new Matrix(1000, 1000);

    // Fill matrices with random data
    for (let i = 0; i < 1000; i++) {
        for (let j = 0; j < 1000; j++) {
            m1.set(i, j, Math.random());
            m2.set(i, j, Math.random());
        }
    }

    console.time('Matrix multiplication');
    const result = m1.multiply(m2);
    console.timeEnd('Matrix multiplication');

    // Benchmark
    console.time('Fibonacci');
    const fib = benchmark_fibonacci(50);
    console.timeEnd('Fibonacci');
    console.log('Fibonacci(50) =', fib);

    // DOM interaction
    greet('WebAssembly');
}

main();
*/