🚧 WIP Wednesday: Experimentando con WebAssembly para Optimización de Performance

Los miércoles compartimos proyectos en desarrollo y experimentos tecnológicos. Hoy exploramos un POC (Proof of Concept) con WebAssembly para optimizar operaciones computacionalmente intensivas en el navegador.

:bullseye: El Problema a Resolver

Las aplicaciones web modernas necesitan procesar grandes volúmenes de datos en tiempo real: análisis de imágenes, cálculos financieros, procesamiento de audio, o manipulación de datasets masivos. JavaScript, aunque optimizado, tiene limitaciones de performance para estas tareas.

:high_voltage: WebAssembly Como Solución

WebAssembly (WASM) permite ejecutar código compilado cerca de la velocidad nativa en el navegador. La hipótesis del experimento: combinar JavaScript para UI y WASM para computación intensiva puede mejorar significativamente el rendimiento.

:hammer_and_wrench: Estructura del POC

Caso de estudio: Sistema de procesamiento de imágenes para filtros en tiempo real

Stack experimental:

  • Rust: Para lógica de procesamiento (compila a WASM)
  • wasm-pack: Para generar bindings JavaScript
  • Canvas API: Para renderizado de imágenes
  • Web Workers: Para no bloquear el hilo principal

:memo: Implementación Paso a Paso

1. Función de procesamiento en Rust:

// lib.rs
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub struct ImageProcessor {
    width: u32,
    height: u32,
    data: Vec,
}

#[wasm_bindgen]
impl ImageProcessor {
    #[wasm_bindgen(constructor)]
    pub fn new(width: u32, height: u32, data: Vec) -> ImageProcessor {
        ImageProcessor { width, height, data }
    }
    
    #[wasm_bindgen]
    pub fn apply_blur(&mut self, radius: u32) {
        // Implementación optimizada de blur gaussiano
        let mut result = vec![0u8; self.data.len()];
        
        for y in 0..self.height {
            for x in 0..self.width {
                let mut r = 0u32;
                let mut g = 0u32;
                let mut b = 0u32;
                let mut count = 0u32;
                
                // Kernel de convolución optimizado
                for dy in -(radius as i32)..(radius as i32) {
                    for dx in -(radius as i32)..(radius as i32) {
                        let nx = x as i32 + dx;
                        let ny = y as i32 + dy;
                        
                        if nx >= 0 && nx < self.width as i32 && 
                           ny >= 0 && ny < self.height as i32 {
                            
                            let idx = ((ny * self.width as i32 + nx) * 4) as usize;
                            r += self.data[idx] as u32;
                            g += self.data[idx + 1] as u32;
                            b += self.data[idx + 2] as u32;
                            count += 1;
                        }
                    }
                }
                
                let idx = ((y * self.width + x) * 4) as usize;
                result[idx] = (r / count) as u8;
                result[idx + 1] = (g / count) as u8;
                result[idx + 2] = (b / count) as u8;
                result[idx + 3] = self.data[idx + 3]; // Alpha sin cambios
            }
        }
        
        self.data = result;
    }
    
    #[wasm_bindgen]
    pub fn get_data(&self) -> Vec {
        self.data.clone()
    }
}

2. Integración con JavaScript:

// image-processor.js
import init, { ImageProcessor } from './pkg/image_processor.js';

class WebAssemblyImageProcessor {
    constructor() {
        this.wasmModule = null;
        this.initialized = false;
    }
    
    async initialize() {
        if (!this.initialized) {
            this.wasmModule = await init();
            this.initialized = true;
        }
    }
    
    async processImage(imageData, filterType, intensity) {
        await this.initialize();
        
        const { width, height, data } = imageData;
        const processor = new ImageProcessor(width, height, Array.from(data));
        
        const startTime = performance.now();
        
        switch (filterType) {
            case 'blur':
                processor.apply_blur(intensity);
                break;
            case 'sharpen':
                processor.apply_sharpen(intensity);
                break;
            default:
                throw new Error(`Unknown filter: ${filterType}`);
        }
        
        const processedData = processor.get_data();
        const duration = performance.now() - startTime;
        
        return {
            data: new Uint8ClampedArray(processedData),
            width,
            height,
            processingTime: duration
        };
    }
}

3. Worker para procesamiento no bloqueante:

// image-worker.js
import { WebAssemblyImageProcessor } from './image-processor.js';

const processor = new WebAssemblyImageProcessor();

self.onmessage = async function(e) {
    const { imageData, filterType, intensity, requestId } = e.data;
    
    try {
        const result = await processor.processImage(imageData, filterType, intensity);
        
        self.postMessage({
            type: 'success',
            requestId,
            result
        });
        
    } catch (error) {
        self.postMessage({
            type: 'error',
            requestId,
            error: error.message
        });
    }
};

:bar_chart: Resultados Preliminares

Pruebas con imágenes de 1920x1080 (filtro blur, radius 5):

JavaScript puro:

  • Tiempo de procesamiento: ~2,400ms
  • Bloqueo del hilo principal: Total
  • Uso de memoria: Alto (múltiples copias de array)

WebAssembly + Worker:

  • Tiempo de procesamiento: ~380ms (6.3x más rápido)
  • Bloqueo del hilo principal: Ninguno
  • Uso de memoria: Optimizado

:magnifying_glass_tilted_left: Observaciones del Experimento

:white_check_mark: Ventajas encontradas:

  • Performance dramática: 6x mejora en operaciones intensivas
  • Predictibilidad: Tiempos de ejecución más consistentes
  • Paralelización: Fácil integración con Web Workers
  • Reutilización: Mismo código Rust para server y client

:warning: Desafíos identificados:

  • Overhead inicial: Carga y compilación del módulo WASM
  • Debugging: Herramientas limitadas comparado con JS
  • Transferencia de datos: Copiar arrays grandes entre JS y WASM
  • Tamaño del bundle: Archivo WASM agrega ~200KB

:video_game: Casos de Uso Prometedores

:artist_palette: Editores gráficos web:
Filtros, transformaciones, y efectos en tiempo real sin lag perceptible.

:bar_chart: Visualización de datos:
Procesamiento de datasets masivos para gráficos interactivos.

:musical_note: Audio processing:
Efectos de audio, análisis de frecuencia, y síntesis en tiempo real.

:abacus: Simulaciones científicas:
Cálculos matemáticos complejos que requieren precisión y velocidad.

:rocket: Próximos Pasos del Experimento

Semana 1-2:

  • Implementar más filtros (sharpen, edge detection, color grading)
  • Optimizar transferencia de datos con SharedArrayBuffer
  • Medir impacto en diferentes dispositivos

Semana 3-4:

  • Comparar con bibliotecas existentes (OpenCV.js)
  • Implementar streaming para imágenes grandes
  • Crear benchmark suite automatizado

:hammer_and_wrench: Herramientas de Desarrollo

Setup del entorno:

# Instalar Rust y wasm-pack
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
cargo install wasm-pack

# Crear proyecto
wasm-pack new image-processor
cd image-processor

# Build para web
wasm-pack build --target web --out-dir pkg

# Test de performance
wasm-pack test --chrome --headless

:light_bulb: Lecciones Aprendidas Hasta Ahora

:bullseye: WebAssembly brilla cuando:

  • Operaciones computacionalmente intensivas
  • Algoritmos con loops anidados pesados
  • Necesidad de performance predecible
  • Reutilización de código existente en C/C++/Rust

:mobile_phone: JavaScript sigue siendo mejor para:

  • Manipulación del DOM
  • Lógica de aplicación y estado
  • Interacción con APIs web
  • Prototipado rápido

:speech_balloon: ¿Qué experimentos tienen en desarrollo? ¿Han probado WebAssembly en sus proyectos? ¿Qué POCs les han dado resultados sorprendentes? Compartamos nuestros WIPs para inspirarnos mutuamente.

wipwednesday webassembly performance rust #ExperimentalTech #POC