Skip to content

[Feat] [Intel] lora: Add intel_xpu LoRA backend - #44769

Open
srujan-thomdapu wants to merge 5 commits into
sgl-project:mainfrom
srujan-thomdapu:dev/srujan/lora_backend
Open

srujan-thomdapu wants to merge 5 commits into
sgl-project:mainfrom
srujan-thomdapu:dev/srujan/lora_backend

Conversation

@srujan-thomdapu

@srujan-thomdapu srujan-thomdapu commented Oct 16, 2026 •

Copy link
Copy Markdown

[Feat] [Intel] lora: Add intel_xpu LoRA backend

Motivation

On Intel XPU, multi-LoRA serving currently has to use the generic triton, csgmv, or torch_native backends. This PR adds an intel_xpu LoRA backend that runs the native Intel XPU LoRA kernels from sgl-kernel-xpu on both the prefill and decode paths.

Usage:

python -m sglang.launch_server \
    --model-path <base-model> \
    --device xpu \
    --enable-lora \
    --lora-paths <name>=<adapter-path> \
    --lora-backend intel_xpu

Dependency: sgl-kernel-xpu

The backend imports these ops from sgl_kernel (sgl-kernel-xpu):

Op Used for PR (merged and closed)
embedding_lora_a_fwd Embedding LoRA A sgl-project/sgl-kernel-xpu#207
sgemm_lora_a_fwd LoRA A shrink, prefill sgl-project/sgl-kernel-xpu#262
sgemm_lora_b_fwd LoRA B expand, prefill sgl-project/sgl-kernel-xpu#356
qkv_lora_b_fwd Fused QKV LoRA B, prefill sgl-project/sgl-kernel-xpu#393
gate_up_lora_b_fwd Fused gate/up LoRA B, prefill sgl-project/sgl-kernel-xpu#490
chunked_sgmv_lora_shrink_fwd LoRA A shrink, decode sgl-project/sgl-kernel-xpu#506
chunked_sgmv_lora_expand_fwd LoRA B expand, decode (single, QKV, and gate/up slices) sgl-project/sgl-kernel-xpu#523

These kernels especially decode kernels are optimized. To get the best performance, build sgl-kernel-xpu from the PR below:

This PR should merge after the above PR got merged in sgl-kernel-xpu.

Modifications

New backend: python/sglang/srt/lora/backend/intel_xpu_backend.py (IntelXpuLoRABackend)

  • Prefill (contiguous batch metadata, permutation is None): per-shape XPU kernels (sgemm_lora_a_fwd, sgemm_lora_b_fwd, qkv_lora_b_fwd, gate_up_lora_b_fwd).
  • Decode: tokens are sorted by adapter (compute_sgemm_routing), and the chunked-SGMV shrink/expand pair are gather fused DPAS kernels. The shrink output layout (num_tokens, num_slices * max_rank) is exactly the expand's input, so decode stays on XPU from A to B. The expand fuses per-adapter scaling and the base_output residual.
  • Supports decode and prefill graph capture (supports_prefill_cuda_graph = True, 32 fixed prefill segment slots). Batches with more requests fall back to eager prefill.
  • Batch preparation (segment routing, lm_head pruning, graph metadata) follows the triton backend's LoRABatchInfo layout.

Registration and CLI

  • lora/backend/lora_registry.py: register intel_xpu.
  • arg_groups/choices.py: add LORA_BACKEND_CHOICES (including intel_xpu). arg_groups/fields/lora.py now reads --lora-backend choices from it instead of a hardcoded list, and server_args.py re-exports it with the other choice lists.

Validation

  • arg_groups/validation_hook.py: validate_device_lora_backend rejects --lora-backend intel_xpu without --device xpu at argument-check time. Without it, the error only appears later as an import failure inside LoRAManager init. This mirrors the existing --sampling-backend intel_xpu check.

Tests

  • test/registered/unit/server_args/test_server_args.py: TestIntelXpuLoRABackend (CPU CI) checks that the backend is registered and selectable from the CLI, rejected off XPU, and accepted on XPU.
  • test/manual/lora/test_lora_xpu_basics.py: add intel_xpu to DENSE_BACKENDS, so it runs the HF+PEFT logprob comparison and the multi-LoRA batching / drainer routing tests next to triton, csgmv, and torch_native.

Accuracy Tests

Models and adapters are the ones defined for test/manual/lora/test_lora_backend.py. Each case runs 4 engines (SRT+LoRA, SRT base, HF+LoRA, HF base) with --lora-backend intel_xpu and compares top input/output logprobs against HF plus ROUGE-L of the decoded text. fp16, max_new_tokens=32, 2 prompts per case (values are prompt 0 / prompt 1); limits: logprob diff < 1e-1, ROUGE-L 1.0.

Base model Adapter Max prefill logprob diff Max decode logprob diff ROUGE-L
Llama-3.1-8B-Instruct nvidia/llama-3.1-nemoguard-8b-topic-control 0.0155 / 0.0339 0.0310 / 0.0234 1.0 / 1.0
Llama-2-7b-hf winddude/wizardLM-LlaMA-LoRA-7B 0.0161 / 0.0312 0.0310 / 0.0312 1.0 / 1.0
Llama-3.1-8B-Instruct algoprog/fact-generation-llama-3.1-8b-instruct-lora 0.0177 / 0.0312 0.0312 / 0.0301 1.0 / 1.0

Speed Tests and Profiling

Llama-3.3-70B-Instruct

Setup

  • Model: meta-llama/Llama-3.3-70B-Instruct, --tp-size 8 on 8 Intel XPU cards, --dtype bfloat16, --attention-backend triton, --disable-radix-cache, graph capture on.
  • LoRA: 4 bf16 adapters (adamkarvonen/llama3.3_70b_shuffled_e3_v2, jeongseokoh/llama3.3_70b_sft, jeongseokoh/llama3.3_70b_speed60_bos, vineetdaniels/llama33_70b_icd_cpt), --max-loras-per-batch 4, adapters drawn uniformly per request. base is the same model served with LoRA disabled.
  • Client: SGLang serving benchmark, random dataset with lengths pinned (--random-range-ratio 1.0, ignore_eos on, so every request emits exactly output_len tokens), request rate inf, fixed concurrency. Every run completed all requests with exactly num_requests * output_len generated tokens.

prefill_4k_4lora: 512 in / 8 out, 128 requests, concurrency 8, --chunked-prefill-size 4096, --max-running-requests 8

Backend Total tok/s Mean TTFT (ms) P99 TTFT (ms) Mean E2E (ms)
base (no LoRA) 1547.27 2014.09 2290.23 2681.61
csgmv 1145.49 2849.50 3163.06 3618.87
triton 1121.21 2915.09 3210.81 3694.60
intel_xpu* 1295.14 2337.53 2881.13 3203.76

intel_xpu vs csgmv: total throughput +13.1%, mean TTFT -18.0%.

decode_128_4lora: 16 in / 64 out, 3000 requests, concurrency 128, --max-running-requests 128

Backend Total tok/s Output tok/s Mean TPOT (ms) P99 TPOT (ms) Mean TTFT (ms) Mean E2E (ms)
base (no LoRA) 1273.85 1031.98 104.67 118.98 1204.15 7798.18
csgmv 833.90 675.56 163.75 178.60 1578.59 11894.63
triton 796.88 645.58 172.12 184.33 1602.02 12445.56
intel_xpu* 979.72 793.69 124.45 146.70 2299.36 10139.78

intel_xpu vs csgmv: total throughput +17.5%, mean TPOT -24.0%.

mixed_1k_4lora: 1024 in / 128 out, 400 requests, concurrency 20, --chunked-prefill-size 4096, --max-running-requests 20

Backend Total tok/s Output tok/s Mean TTFT (ms) Mean TPOT (ms) P99 TPOT (ms) Mean E2E (ms)
base (no LoRA) 1150.92 127.99 7195.63 100.81 151.64 19998.48
csgmv 792.66 88.15 9012.90 157.68 221.34 29038.33
triton 762.58 84.80 9158.18 165.56 230.26 30184.13
intel_xpu 967.28 107.57 8010.22 124.29 181.46 23795.51

intel_xpu vs csgmv: total throughput +22.0%, mean TPOT -21.2%, mean TTFT -11.1%.

Cost of attaching adapters. Each row is the LoRA run relative to the same base model served with no adapters, so a negative throughput delta is the expected price of LoRA.

Scenario Backend Δ total throughput vs base Δ mean E2E latency vs base
prefill_4k_4lora csgmv −26.0 % +35.0 %
prefill_4k_4lora triton −27.5 % +37.8 %
prefill_4k_4lora intel_xpu −16.3 % +19.5 %
decode_128_4lora csgmv −34.5 % +52.5 %
decode_128_4lora triton −37.4 % +59.6 %
decode_128_4lora intel_xpu −23.1 % +30.0 %
mixed_1k_4lora csgmv −31.1 % +45.2 %
mixed_1k_4lora triton −33.7 % +50.9 %
mixed_1k_4lora intel_xpu −16.0 % +19.0 %

In prefill_4k_4lora each request emits only 8 tokens, so mean TPOT is dominated by decode steps queued behind other requests' prefill chunks (the base run shows the same effect: P99 ITL 1888 ms); total throughput and TTFT are the meaningful metrics there.

In decode_128_4lora, mean TTFT is higher with intel_xpu than with csgmv (2299 vs 1579 ms, +46 %). Each request has a 16-token prompt, so TTFT there is mostly time spent queueing for a slot in the full decode batch rather than prefill compute (base TTFT also grows with concurrency), and it is 23 % of intel_xpu's end-to-end latency. Mean E2E latency is still 15 % lower than csgmv's (10140 vs 11895 ms); total throughput, TPOT, and E2E latency are the meaningful metrics for decode scenarios.

The base (no-LoRA) path is unchanged by this PR: it only adds the intel_xpu LoRA backend, its --lora-backend choice, and the --device xpu check.

Llama-3.1-8B-Instruct

Setup

  • Model: meta-llama/Llama-3.1-8B-Instruct, --tp-size 8 on 8 Intel XPU cards, --dtype bfloat16, --attention-backend triton, --disable-radix-cache, graph capture on.
  • LoRA: 8 adapters (adamkarvonen/llama3_1_8b_on_llama3_1_8b_counterfactual_24k_e1_kl0, dsfsi/llama_3.1_8b_instruct-lora-r64-eng-{amh,hau,lug,yor,zul}, r-three/lora_baseline_10_lr3e-4_step100_rank64_race_high, r-three/lora_baseline_lr3e-4_step400_rank64_qa_wikidata), --max-loras-per-batch 8, adapters drawn uniformly per request. base is the same model served with LoRA disabled.
  • Client: same as above (pinned lengths, ignore_eos, request rate inf, fixed concurrency). Prefill scenarios: long input, 8 output tokens, chunked-prefill size grown with the input. Decode scenarios: 16 in / 96 out, decode batch = concurrency, --chunked-prefill-size 8192 --max-prefill-tokens 16384. Every run completed all requests with exactly num_requests * output_len generated tokens.

Total throughput (tok/s)

Scenario Workload (in / out, concurrency) base (no LoRA) csgmv triton intel_xpu intel_xpu vs csgmv intel_xpu vs triton
prefill_4k_8lora 512 / 8, 16 8576 5474 5321 7153 1.31x 1.34x
prefill_8k_8lora 1024 / 8, 16 8933 5985 5814 7773 1.30x 1.34x
prefill_12k_8lora 1536 / 8, 16 8500 5887 5735 7559 1.28x 1.32x
prefill_16k_8lora 2048 / 8, 16 7984 5690 5537 7214 1.27x 1.30x
prefill_24k_8lora 3072 / 8, 24 7069 5284 5143 6531 1.24x 1.27x
prefill_65k_8lora 4096 / 8, 32 6223 4820 4701 5826 1.21x 1.24x
decode_64_8lora 16 / 96, 64 2528 1509 1568 1819 1.21x 1.16x
decode_128_8lora 16 / 96, 128 4704 2911 2944 3388 1.16x 1.15x
decode_256_8lora 16 / 96, 256 7235 4485 4135 5355 1.19x 1.29x
decode_512_8lora 16 / 96, 512 7688 5190 4781 6041 1.16x 1.26x
decode_1024_8lora 16 / 96, 1024 8770 6044 5407 7022 1.16x 1.30x
decode_2048_8lora 16 / 96, 2048 8913 6229 5679 7242 1.16x 1.28x

Cost of attaching adapters. Each row is the LoRA run relative to the same base model served with no adapters.

Scenario Backend Δ total throughput vs base Δ mean E2E latency vs base
prefill_4k_8lora csgmv −36.2 % +56.8 %
prefill_4k_8lora triton −38.0 % +61.3 %
prefill_4k_8lora intel_xpu −16.6 % +19.9 %
prefill_8k_8lora csgmv −33.0 % +49.3 %
prefill_8k_8lora triton −34.9 % +53.7 %
prefill_8k_8lora intel_xpu −13.0 % +14.9 %
prefill_12k_8lora csgmv −30.7 % +44.4 %
prefill_12k_8lora triton −32.5 % +48.3 %
prefill_12k_8lora intel_xpu −11.1 % +12.5 %
prefill_16k_8lora csgmv −28.7 % +40.3 %
prefill_16k_8lora triton −30.6 % +44.2 %
prefill_16k_8lora intel_xpu −9.7 % +10.7 %
prefill_24k_8lora csgmv −25.2 % +33.7 %
prefill_24k_8lora triton −27.2 % +37.4 %
prefill_24k_8lora intel_xpu −7.6 % +8.2 %
prefill_65k_8lora csgmv −22.5 % +29.1 %
prefill_65k_8lora triton −24.5 % +32.4 %
prefill_65k_8lora intel_xpu −6.4 % +6.8 %
decode_64_8lora csgmv −40.3 % +67.6 %
decode_64_8lora triton −38.0 % +61.4 %
decode_64_8lora intel_xpu −28.0 % +39.0 %
decode_128_8lora csgmv −38.1 % +61.8 %
decode_128_8lora triton −37.4 % +59.9 %
decode_128_8lora intel_xpu −28.0 % +39.0 %
decode_256_8lora csgmv −38.0 % +61.4 %
decode_256_8lora triton −42.8 % +75.2 %
decode_256_8lora intel_xpu −26.0 % +35.0 %
decode_512_8lora csgmv −32.5 % +48.4 %
decode_512_8lora triton −37.8 % +61.1 %
decode_512_8lora intel_xpu −21.4 % +27.3 %
decode_1024_8lora csgmv −31.1 % +45.4 %
decode_1024_8lora triton −38.3 % +62.6 %
decode_1024_8lora intel_xpu −19.9 % +25.1 %
decode_2048_8lora csgmv −30.1 % +43.8 %
decode_2048_8lora triton −36.3 % +57.8 %
decode_2048_8lora intel_xpu −18.7 % +23.7 %

The prefill rows emit only 8 tokens per request, so mean TPOT there is dominated by decode steps queued behind prefill chunks; total throughput and TTFT are the meaningful metrics.

In the decode rows, mean TTFT is higher with intel_xpu than with csgmv (+25 % to +36 %). Each request has a 16-token prompt, so TTFT there is mostly time spent queueing for a slot in the full decode batch rather than prefill compute (base TTFT also grows with concurrency, 133 ms at 64 to 1968 ms at 2048), and it is 7 % to 14 % of intel_xpu's end-to-end latency. Mean E2E latency is still 14 % to 17 % lower than csgmv's at every concurrency; total throughput, TPOT, and E2E latency are the meaningful metrics for decode scenarios.

Llama-3.1-8B-Instruct: full metrics per scenario

prefill_4k_8lora: 512 in / 8 out, 512 requests, concurrency 16, --max-running-requests 16, --chunked-prefill-size 4096

Backend Total tok/s Output tok/s Mean TTFT (ms) Mean TPOT (ms) P99 TPOT (ms) Mean E2E (ms)
base (no LoRA) 8575.80 132.19 594.71 53.07 120.87 966.18
csgmv 5473.79 84.37 896.90 88.29 210.58 1514.92
triton 5321.24 82.02 934.53 89.13 218.20 1558.43
intel_xpu 7153.01 110.26 688.58 67.17 161.28 1158.75

prefill_8k_8lora: 1024 in / 8 out, 512 requests, concurrency 16, --max-running-requests 16, --chunked-prefill-size 8192

Backend Total tok/s Output tok/s Mean TTFT (ms) Mean TPOT (ms) P99 TPOT (ms) Mean E2E (ms)
base (no LoRA) 8932.54 69.31 1226.52 88.30 239.53 1844.63
csgmv 5984.71 46.44 1800.92 136.17 358.20 2754.15
triton 5813.70 45.11 1866.47 138.40 368.71 2835.26
intel_xpu 7772.68 60.31 1384.02 105.16 271.81 2120.16

prefill_12k_8lora: 1536 in / 8 out, 512 requests, concurrency 16, --max-running-requests 16, --chunked-prefill-size 12288

Backend Total tok/s Output tok/s Mean TTFT (ms) Mean TPOT (ms) P99 TPOT (ms) Mean E2E (ms)
base (no LoRA) 8499.93 44.07 1993.51 129.83 380.11 2902.30
csgmv 5886.60 30.52 2843.23 192.63 550.32 4191.67
triton 5734.80 29.73 2935.04 195.38 563.83 4302.71
intel_xpu 7558.81 39.19 2211.41 150.32 423.93 3263.65

prefill_16k_8lora: 2048 in / 8 out, 512 requests, concurrency 16, --max-running-requests 16, --chunked-prefill-size 16384

Backend Total tok/s Output tok/s Mean TTFT (ms) Mean TPOT (ms) P99 TPOT (ms) Mean E2E (ms)
base (no LoRA) 7984.34 31.08 2871.09 177.82 542.82 4115.83
csgmv 5689.70 22.15 3977.16 257.05 764.88 5776.53
triton 5537.26 21.56 4104.37 261.61 784.88 5935.65
intel_xpu 7213.77 28.08 3145.05 201.52 599.01 4555.72

prefill_24k_8lora: 3072 in / 8 out, 512 requests, concurrency 24, --max-running-requests 24, --chunked-prefill-size 24576

Backend Total tok/s Output tok/s Mean TTFT (ms) Mean TPOT (ms) P99 TPOT (ms) Mean E2E (ms)
base (no LoRA) 7068.63 18.37 6639.32 528.25 1417.64 10337.06
csgmv 5284.10 13.73 8946.63 696.42 1898.17 13821.60
triton 5142.90 13.36 9169.42 718.82 1949.77 14201.19
intel_xpu 6530.95 16.97 7213.26 566.93 1532.54 11181.80

prefill_65k_8lora: 4096 in / 8 out, 384 requests, concurrency 32, --max-running-requests 32, --chunked-prefill-size 65536

Backend Total tok/s Output tok/s Mean TTFT (ms) Mean TPOT (ms) P99 TPOT (ms) Mean E2E (ms)
base (no LoRA) 6222.92 12.13 14149.63 991.77 2810.67 21091.99
csgmv 4819.71 9.40 18803.90 1204.27 3630.25 27233.81
triton 4701.01 9.17 19061.49 1265.68 3475.37 27921.22
intel_xpu 5826.02 11.36 15646.00 983.25 2998.77 22528.73

decode_64_8lora: 16 in / 96 out, 6000 requests, concurrency 64, --max-running-requests 64

Backend Total tok/s Output tok/s Mean TTFT (ms) Mean TPOT (ms) P99 TPOT (ms) Mean E2E (ms)
base (no LoRA) 2528.11 2186.47 133.41 28.04 29.98 2796.84
csgmv 1509.38 1305.41 198.26 47.26 49.32 4688.00
triton 1567.91 1356.03 226.74 45.12 47.01 4512.89
intel_xpu 1819.21 1573.37 263.42 38.15 39.74 3888.00

decode_128_8lora: 16 in / 96 out, 6000 requests, concurrency 128, --max-running-requests 128

Backend Total tok/s Output tok/s Mean TTFT (ms) Mean TPOT (ms) P99 TPOT (ms) Mean E2E (ms)
base (no LoRA) 4704.20 4068.49 162.94 29.88 30.75 3001.27
csgmv 2911.19 2517.79 284.33 48.12 50.06 4855.87
triton 2944.23 2546.37 287.48 47.50 49.59 4800.12
intel_xpu 3387.62 2929.84 354.43 40.19 42.73 4172.06

decode_256_8lora: 16 in / 96 out, 6000 requests, concurrency 256, --max-running-requests 256

Backend Total tok/s Output tok/s Mean TTFT (ms) Mean TPOT (ms) P99 TPOT (ms) Mean E2E (ms)
base (no LoRA) 7234.76 6257.09 346.01 36.80 38.64 3842.11
csgmv 4485.28 3879.16 557.68 59.40 62.50 6200.81
triton 4135.32 3576.49 564.14 64.92 69.64 6731.51
intel_xpu 5354.80 4631.18 699.71 47.22 52.82 5185.73

decode_512_8lora: 16 in / 96 out, 4000 requests, concurrency 512, --max-running-requests 512

Backend Total tok/s Output tok/s Mean TTFT (ms) Mean TPOT (ms) P99 TPOT (ms) Mean E2E (ms)
base (no LoRA) 7687.61 6648.75 606.55 69.35 74.86 7194.67
csgmv 5190.05 4488.69 949.42 102.36 110.11 10673.84
triton 4780.66 4134.63 999.16 111.46 121.02 11588.01
intel_xpu 6040.55 5224.26 1242.96 83.35 94.93 9161.49

decode_1024_8lora: 16 in / 96 out, 6000 requests, concurrency 1024, --max-running-requests 1024

Backend Total tok/s Output tok/s Mean TTFT (ms) Mean TPOT (ms) P99 TPOT (ms) Mean E2E (ms)
base (no LoRA) 8769.77 7584.66 1004.53 122.04 131.41 12598.38
csgmv 6044.45 5227.63 1630.72 175.62 190.34 18314.19
triton 5407.50 4676.75 1710.56 197.59 214.49 20481.82
intel_xpu 7021.53 6072.67 2212.38 142.59 164.43 15758.23

decode_2048_8lora: 16 in / 96 out, 8000 requests, concurrency 2048, --max-running-requests 2048

Backend Total tok/s Output tok/s Mean TTFT (ms) Mean TPOT (ms) P99 TPOT (ms) Mean E2E (ms)
base (no LoRA) 8912.63 7708.22 1968.37 238.55 259.56 24630.98
csgmv 6229.22 5387.43 2985.65 341.32 372.48 35411.09
triton 5678.70 4911.31 3095.21 376.45 408.66 38857.75
intel_xpu 7242.21 6263.54 3892.29 279.71 318.15 30464.91

CI States

Latest PR Test (Base): ❌ Run #40483093338
Latest PR Test (Extra): ❌ Run #40483093556
Latest PR Test (AMD ROCm 10): ❌ Run #40483093157

@github-actions

This comment has been minimized.

)

# Prefill (contiguous): XPU B-expand.
return xpu_qkv_lora_b_fwd(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These fused B kernels, and the decode chunked expand, read slice p at column p * max_rank and ignore lora_ranks, but LoRAMemoryPool packs a rank-r adapter's stacked A at stride r. Any adapter below --max-lora-rank gets wrong k, v and up deltas; triton masks by rank. Rank-aware kernels, or a max_rank-spaced pool layout?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. Fixed with a max_rank-spaced pool layout rather than rank-aware kernels. The XPU kernels deliberately contract over the full max_rank, so the backend now declares uses_max_rank_spaced_lora_layout and the pool places each stacked A slice at p * max_lora_rank, matching what MoE A already does. This also fixed a related issue: the XPU kernels need zeroed rank tails, but the pool didn't zero them on LRU slot reuse, so the slot is now cleared before load. Triton keeps the packed layout. Added a regression test in test_mem_pool_ep_unit.py.

num_slices=1,
base_output=base_output,
)
return xpu_sgemm_lora_b_fwd(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These kernels also need A rows and B columns past the adapter's rank zeroed, but slot eviction reloads only the first r, so a rank-8 adapter in a slot a rank-16 one used keeps the old tail and part of its delta leaks in. The pool could zero the B tail on load for this backend, like the SGLANG_EXPERIMENTAL_LORA_OPTI path does.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same change addressed in the previous comment covers this too.

return xpu_qkv_lora_b_fwd(
input_x=lora_a_output,
qkv_lora_b=qkv_lora_b,
output_offset=output_offset,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

n_slices reaches the shrink and the decode expand but not this call, and qkv_lora_b_fwd is hard-wired to three slices, so DeepSeek MLA fused_qkv_a_proj_with_mqa (n_slices=2) fails prefill with "output_offset must have 4 elements". Could prefill use chunked_sgmv_lora_expand_fwd with permutation=None?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Prefill now routes to chunked_sgmv_lora_expand_fwd with permutation=None (its contiguous fast path) whenever n_slices != 3, so MLA fused_qkv_a_proj_with_mqa gets the 2-slice expand with its own output_offset. The standard 3-slice q/k/v prefill keeps the CUTLASS qkv_lora_b_fwd. Both kernels derive the segment count from seg_indptr, so the prefill CUDA graph is unaffected.

input_ids=input_ids,
weights=weights,
vocab_size=vocab_size,
max_len=bi.max_len,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

embedding_lora_a_fwd in sglang-kernel-xpu 0.4.0, the wheel pinned in pyproject_xpu.toml, has no max_len, so any adapter on embed_tokens raises TypeError here. That and the per-call .item() checks that break XPU graph capture are only fixed in unmerged sgl-kernel-xpu#550, so the pin bump has to land with this PR.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. This PR depends on sgl-kernel-xpu#550. Once it merges, I will bump the pyproject_xpu.toml pin in this PR.

permutation=torch.zeros(max_tokens, dtype=torch.int32),
)

def init_prefill_cuda_graph_batch_info(self, max_num_tokens: int):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LoRAManager.init_prefill_cuda_graph_batch_info passes max_num_requests= as a keyword, so --cuda-graph-backend-prefill breakable or full with this backend dies with TypeError at capture. Accept it, and probably size the slots from it like triton does instead of the fixed 32.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. init_prefill_cuda_graph_batch_info now takes max_num_requests and sizes the slots from it like triton (max_num_tokens when None); the fixed 32-slot constant is gone, so the graph covers the captured request capacity instead of falling back to eager prefill past 32 requests.

else:
max_len = (
# Calculate max_len from the CPU copy to avoid D2H transfer.
max(forward_batch.extend_seq_lens_cpu)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TARGET_VERIFY counts as is_extend() but leaves extend_seq_lens_cpu None, so eager verify with EAGLE plus LoRA (bs over the graph cap, or graphs off) crashes on max(None). Triton uses get_batch_token_counts and generate_sequence_lengths; this is an older copy of its prepare_lora_batch, so subclassing it fixes that.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. IntelXpuLoRABackend now subclasses TritonLoRABackend.

)

DENSE_BACKENDS = [*BACKENDS, "torch_native"]
DENSE_BACKENDS = [*BACKENDS, "torch_native", "intel_xpu"]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DENSE_BACKENDS only feeds the accuracy and batching tests, which start with rank-8 adapters, so max_lora_rank equals the rank and nothing is evicted. The embedding, eviction and dynamic-load tests run rank 8 under max 16 and would hit the stride and max_len failures; can intel_xpu get variants there like torch_native?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added intel_xpu variants for dynamic load, eviction (lru/fifo) and embedding. The eviction and embedding cases inferred max rank = adapter rank, and the embedding test targets q/v rather than embed_tokens, so on their own they wouldn't have hit either failure. To cover them directly I added test_lora_below_max_rank_matches_hf (rank 8 under max 16 vs HF, all dense backends; catches the stride bug) and test_eviction_mixed_rank_intel_xpu (rank-8 reload into a slot a rank-16 adapter used; catches stale tails).

@github-actions

This comment has been minimized.

@srujan-thomdapu
srujan-thomdapu force-pushed the dev/srujan/lora_backend branch from dbf5196 to eee0377 Compare October 27, 2026 09:44
@github-actions

Copy link
Copy Markdown
Contributor

Required checks failed on eee0377; this PR cannot be merged until they pass.

Lint (run)
Run pre-commit run --all-files locally and push the result. The run log shows any other failing step.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants