Skip to content

Choosing an execution model

Choose using your workload, dtype, array size and data lifetime. Small arrays may be faster in JavaScript; native, Wasm, fusion and Workers each have overhead. Measure the complete application path before choosing a more complex API.

Start with the data lifetime

WorkloadStarting pointMain cost to measure
One operation on existing TypedArraysOrdinary callsBinding and allocation overhead
Repeated operations on the same dataResident buffersUpload, reuse and final download
A supported chain of elementwise operationsFused pipelinesCompilation and complete execution
Large pipelines that block interactionWorkersSubmission, total latency and responsiveness

Reuse an engine and pass { out } when an existing destination is available. Avoid repeatedly creating engines, arenas or executors inside a hot loop.

Native and Wasm transfers

Native TypedArray calls borrow existing memory synchronously. Ordinary Wasm calls copy inputs into backend memory and copy results back. Resident buffers let you upload once, reuse data across calls and download the final result.

Include arena creation, allocation, upload, download and disposal when those happen per job. A fast resident kernel alone does not predict the cost of that lifecycle. Use a persistent arena when data can remain resident across frames or requests.

Fused pipelines

Fusion traverses the input once and avoids full-size intermediate arrays. Compile a plan once and reuse the compiled object. Repeated cache lookups still normalize the definition.

Constant mul → add → clamp chains have a specialized loop. Other supported plans use a bounded interpreter and can cost more than separate calls. Results for one specialized chain do not predict every pipeline's performance.

Query pipeline.capability or engine.getCapability() to inspect actual execution. Selecting a SIMD backend does not make every operation vectorized, and short inputs or vector tails can still run scalar code.

Workers

Workers keep large calculations off the main thread, but submit() still copies input snapshots synchronously. Startup, dispatch and result delivery add overhead. Reuse an executor and measure submission latency separately from completion time.

Measure event-loop or animation-frame gaps when responsiveness is the goal. A workload may become more responsive while taking longer to finish. For small tasks, synchronous operations may be a better fit.

Worker byte budgets bound admitted task payload, not process RSS. Include caller-owned arrays, engine memory, retained Wasm capacity and garbage collection when assessing application memory use.

Compare equivalent work

JavaScript comparisons must preserve each f32 rounding step and integer wrapping rule. Keep numerical accuracy checks separate from timing. Repeat measurements on the same machine with the same assets and include transfer/allocation costs that occur in production.

See running benchmarks for commands, report formats and measurement boundaries.

Released under the MIT License.