Repository navigation
Add experimental wgpu backend for Candle - #3379
KimHenrikOtte wants to merge 222 commits into
Conversation
Tanh and Matmul are supported. To copy data from GpuStorage to Cpu a new function to_device_async was added (async is needed in wasm) - There is no support for: Storage Layout, Conv2d, Compare, Other Types than F32.
- added a few basic operations(relu, sin, cos, add, sub, etc.)
-Added Debug measurement Infos
- wgpu is now an optional feature
- Improved Shader Loading (only the file needed fill be loadad , - Performance Improvement due to chaining multiple commands in a single computePass
-erf and gelu_erf, -fixed index_select implementation error
-Fix Build Script Error - Fix Device MinBuffer Size
- changed function to async for llama2-wasm example
added wasm debug messages
… for every Operation
-marked function, that may be called by an async version as deprecated -fixed a conv2d problem -added conc1d
- changed examples to work with wgpu(not tested so far)
changed conv2d kernel size, added matmul1_end
- Simplified quantized shader loading - Fixed `MutexGuard` across async methods; on WASM, the locks will be dropped before the await point - Added multi-thread test
# Conflicts: # candle-core/Cargo.toml
- It previously appeared to enable the `wgpu` feature across the entire workspace. - format document
|
Thank you @KimHenrikOtte for this. I have tried your wgpu backend and it works amazing.
You can reproduce this for phi example with phi_1_5_Q80 and puffin Q80 models. Fix was a one line change I have put here - vandana-rajan@caf8bba I can push this as a PR once your branch gets merged. |
I tried to reproduce the error using: cargo run --example phi --features="wgpu" --release -- --model 1.5 --quantized --prompt "A skier slides down a frictionless slope of height 40m and length 80m. What's the skier speed at the bottom?"This seems to work on my side. I assume you used the In general, the implementation in the browser may be stricter than the native drivers used by wgpu. Interestingly, I am currently having some issues running: or because |
- added quantized tests to candle-wasm-tests
|
Thank you @vandana-rajan for pointing out the error. Generally, Chrome’s implementation is stricter about the format of the WASM file. I tested this branch in In this branch, the I fixed the error you mentioned and also found two similar locations where a parenthesis was missing. |
|
Thats great. Thank you @KimHenrikOtte for the awesome work and prompt response. |
Remove cpu_flash_test and slow tests for WASM.
PR huggingface#3379 was based on 0.9.2 but the brainwires-framework already declares candle ^0.10 (matching v0.10-wgpu). Bump every workspace member's version + every internal version pin to 0.10.2 so the framework consumer resolves cleanly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…aming PR huggingface#3379's regular `alloc_from_bytes` requires the entire tensor in linear memory before upload, which is fine for native but on wasm32 caps tensor size at the linear-memory limit (~4 GB total budget). The chat-pwa loader needed the old v0.10-wgpu pattern — pre-allocate, chunk-write, wrap — to stream a 805 MB embedding tensor in 64 MB slices with a tiny bounded peak. Add two paired public methods on `wgpu_compute_layer::WgpuDevice` (plus matching wrappers on `candle_core::WgpuDevice`): - `alloc_uninit_storage_eager(dtype, byte_size)` — allocates a `BufferReferenceId` AND eagerly forces `cache.search_buffer` to materialize the underlying `wgpu::Buffer` now (regular `alloc_uninit_size` defers buffer creation to first dispatch, which doesn't work for streaming uploads). - `write_to_storage_at(storage, byte_offset, data)` — calls `queue.write_buffer` against the eager buffer. `byte_offset` and `data.len()` must be 4-byte aligned per wgpu spec. Internal plumbing in `cache::ModelCache`: `alloc_eager_storage` mirrors `create_buffer_reference_init` minus the data-upload step; `write_to_eager_storage` looks up the cached buffer through the existing `BufferReferenceId → CachedBufferId → wgpu::Buffer` resolution chain. `max_memory_size` accessor is re-exposed so the device-level allocation guard can validate. The chat-pwa `load_tensor_to_gpu` will rewire to this API in a follow-up commit, restoring 64 MiB peak from the current ~805 MB peak. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…part 4) Adds `quantized_gemma4::ModelWeights` — the QMatMul/GGUF counterpart to `gemma4::text::TextModel`. Mirrors `quantized_gemma3.rs` structurally and reads weights directly from `gguf_file::Content::tensor` so the entire matmul path uses the quantized kernels (PR huggingface#3379's `q4_k.pwgsl`/`q5_k.pwgsl`/etc on WGPU, candle's CPU dequant-on-fly elsewhere). What's ported in this initial slice: - RmsNorm input + post-attention + pre-feedforward + post-feedforward - GQA self-attention with q_norm / k_norm / v_norm - p-RoPE (partial 25%) on full layers, standard RoPE on sliding - SwiGLU MLP (gate / up / down) - KvCache (Normal for full layers, Rotating for sliding) - KvCache reset across the whole stack via `clear_kv_cache` What's gated off (the file-doc lists each + corresponding `Gemma4TextConfig.disable_*` flag): - Per-Layer Embeddings (PLE) - AltUp (Alternating Updates) - LAuReL (Learned Augmented Residual Layer) - KV-share donor / receiver attention - layer_scalar - activation sparsity (Gaussian-topk) Output won't match Gemma 4 reference exactly with the auxiliary towers off — this scaffold is the keystone for Phase 5's perf gain to actually reach inference. Adding the auxiliary towers is straightforward Linear → QMatMul translation; the holdout is verifying each tower against a reference output, which needs an Ollama-published gemma4:e2b GGUF + matching reference run. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The branch has been at v0.11-wgpu for a while (PR huggingface#3379 WebGPU backend, flash-attn decode design doc, full QStorage::Wgpu, q4_k/q5_k/q6_k/q8_k WGSL kernels) but the package version was still claiming 0.10.2. Bumping the workspace + the six per-crate Cargo.tomls that don't inherit `version.workspace = true` to advertise the actual version consumers see (downstream pins by git rev anyway, but the version number should reflect reality). Per-crate cascade through workspace deps (candle-core / candle-nn / candle-transformers / candle-datasets / candle-kernels / candle-metal-kernels / candle-wgpu-kernels / wgpu-compute-layer / candle-onnx / candle-ug / candle-flash-attn / candle-flash-attn-v3): all references to `version = "0.10.2"` updated. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
implement load_safetensor function for handling BF16 tensors in WGPU
fixed a native crash in wgpu tests caused by wasm-only logging being used on non-wasm targets made test logging target-aware so wasm uses browser console and native uses normal stdout fixed wgpu test device setup so wasm uses async device creation while native uses the safe native path improved generated wasm test async handling so await inside macro arguments is detected correctly fixed flaky unary_grad expectations by applying lower precision only for wgpu, while keeping strict checks for other backends
Summary
This PR adds an experimental
wgpubackend for Candle, enabling portable GPU compute across native and WASM targets and fixes (#344).Advantages of
wgpucompared to CUDA:Performance Metrics
Included Changes (High Level)
wgpubackend implementationwgpubackend)Notes
If preferred, I can split parts of this into separate PRs (e.g. backend core, kernels, WASM examples, wasm tests).