Repository navigation
Conversation
layers/fused_ew.h, with CPU references and SYCL kernels: - fused_rms: RMSNorm into F32 / BF16 rows (fused_rms_q8 wrote only q8 blobs) - fused_swiglu: the GLU node with SiLU, into BF16 rows or a q8 blob - FUSED_GELU_ERF: an exact-GELU epilogue for fused_linear and qlinear (oneDNN eltwise_gelu_erf); the old bool argument still means tanh - fused_lnorm without affine (null weight and bias) - fused_rope_kv_table: RoPE from per-token cos/sin tables (rope_2d), with a BF16 output so a batch of views can be read in place by the attention modules/fused_lm.h: a Qwen2 / Llama decoder layer as 8 fused nodes. modules/encoder: EncCfg::gelu_erf, and fold_layer_scale for folding LayerScale into resident BF16 weights. tests/test_fused_sycl_ops checks every new node against the CPU reference. pi0.5's actions are bit-identical to before (default and W8A8).
On SYCL with BF16 weights and view-taking flash attention: - flash attention (oneDNN's fused SDPA) on by default on SYCL unless --flash-attn is given; the fused paths below need it - SigLIP as EncCfg::fused blocks, both views in one pass (q, k and v stream into row slices of one q|k|v weight at load) - every SmolLM2 layer (VLM prefix and expert) as 9 fused nodes, K/V written in place into per-layer F16 buffers; cross-attention layers reproject the prefix rows once per request; residual stream F32 - opt-in VLA_SMOLVLA_W8A8=all|vit,vlm,expert: those GEMMs as W8A8 --flash-attn 0, VLA_SMOLVLA_NO_VIT_FUSE=1 / VLA_SMOLVLA_NO_FUSE=1 keep the per-op graphs. Core Ultra 7 356H, 2 views at 512 px, vla-bench p50: 558 ms (--flash-attn 0) -> 259 ms (flash attention) -> 121-131 ms (fused). LIBERO-object x100: 85/100 (flash attention only 88/100, --flash-attn 0 92/100); not significant at n = 100, needs a larger run.
VLA_SMOLVLA_NPU_VISION=<socket> with 2+ views: the last view goes to scripts/npu_vision_worker.py on the Intel NPU while the GPU encodes the others, so vision costs max(GPU, NPU) instead of the sum. Any NPU failure falls back to the GPU for that view. Without the variable the graph and the actions are unchanged (bit-identical). scripts/build_vision_ir.py builds SmolVLA's vision encoder (patch embed, SigLIP blocks, post-LN, pixel shuffle, connector) as an OpenVINO graph straight from the GGUF: it matches the ggml graph to 3e-6 (CPU f32) and runs in 31 ms per view on the NPU, where the graph ggml-openvino exports takes ~330 ms. The worker gains --turbo (NPU_TURBO, keeps the NPU clocked between requests). Core Ultra 7 356H, 512 px, vla-bench p50: 3 views 151.5 -> 135.5 ms (vision 66 -> 50 ms); 2 views 120.8 -> 121 ms (vision 44 -> 39 ms, not a net gain). Actions vs GPU-only: max 2.2e-3, mean 1e-4. LIBERO-object x100, same binary, 2 views: 87/100 with the NPU view (median 122 ms per request) vs 80/100 GPU-only (142 ms); no fallbacks to the GPU.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Depends on the SmolVLA fused PR (patch 2). Opt-in; with the variable unset, SmolVLA's graph and actions are unchanged (bit-identical).
With
VLA_SMOLVLA_NPU_VISION=<socket>and 2 or more camera views:Split: SmolVLA sends its last view to a worker on the Intel NPU, while the GPU encodes the other views in one pass. Vision then costs the slower of the two instead of their sum.
Fallback: if the NPU fails, that view is encoded on the GPU.
scripts/build_vision_ir.py(new): builds SmolVLA's vision encoder as an OpenVINO graph straight from the GGUF: patch embedding, 12 SigLIP blocks, post-LayerNorm, pixel shuffle and connector.scripts/npu_vision_worker.py: serves that graph on the NPU. It gains--turbo(NPU_TURBO): without it the NPU clocks down between a policy loop's requests, and each view costs about twice as much.src/modules/npu_vision.h: the socket client. SmolVLA's vision code becomes oneencode(first, count)helper, shared by both paths.Usage
Results
Core Ultra 7 356H (Panther Lake, Xe3 iGPU + NPU 5), OpenVINO 2026.4.1, NPU driver 1.38, 512 px.
vla-benchp50, back-to-back requests, interleaved over two rounds:LIBERO-object, 10 tasks x 10 episodes per arm, same binary, 2 views (LIBERO's cameras):
vla_predict_check, fixed inputs).Why it is faster, and when
vla-benchshows no net gain. In the LIBERO loop, which has gaps between requests, the median request is 20 ms faster.Verified
-Wall -Wextra(first-party code)ctestpassesvla_predict_checkdiff), or the change ismeant to move it and a LIBERO sweep is below
Archs and backends tested:
Linux x86 / PTL