Qwen 3.6-35B-A3B 在 Intel Arc B70 Pro 上实现 977 tok/s 提示处理与 26.2万上下文窗口
摘要
本文介绍如何使用 llama.cpp 的 SYCL 后端,在 Intel Arc Pro B70 GPU 上使整个模型和 KV 缓存位于显存中,从而实现 Qwen 3.6-35B-A3B 模型每秒超过 60 个 token 的处理速度。
# Llama 基准测试结果
|模型|大小|参数|后端|ngl|线程|类型\_k|类型\_v|fa|测试|t/s|
|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|:-|
|qwen35moe 35B.A3B Q4\_K - Medium|20.81 GiB|34.66 B|SYCL|99|1|q8\_0|q8\_0|1|pp512|977.40 ± 2.02|
|qwen35moe 35B.A3B Q4\_K - Medium|20.81 GiB|34.66 B|SYCL|99|1|q8\_0|q8\_0|1|tg128|70.54 ± 0.12|
我把所有笔记都扔进了一个大语言模型,生成了一篇文章,如果你想复现同样的配置的话。我目前正将其与 oh my pi 一起使用,非常实用。我成功创建了一个设计精良的扑克游戏,没有出现循环、卡死或崩溃。我之前也试过 Intel 的 vllm,但没能让它达到这种单次请求的性能。我看到有一些更新,所以有时间的话会再试一次。很想知道是否有人运行了类似配置,是否有我遗漏的优化,或者有没有其实没用的东西?总想再压榨出更多性能。同时,衷心感谢 llama.cpp 的贡献者以及所有致力于让本地推理成为可能的人们。我能在本地进行这类推理,完全依赖于这些构建和维护它的人。
查看缓存全文
缓存时间: 2026/06/02 15:32
# 驾驭 Battlemage:在 Qwen 3.6 上通过 Intel SYCL 实现稳定 63 Tokens/秒
来源:https://lemongravy.me/articles/intel-gpu-llamacpp/
过去,运行大型本地大语言模型(LLM)通常需要对 Nvidia 硬件进行大规模投资。然而,随着 Intel Battlemage 和 Arc Pro 等独立 GPU 架构的到来,消费级 Intel 硬件已成为极具竞争力的替代方案。
在 Intel GPU 上实现最佳性能并非即插即用。要让 Qwen 3.6 这样的 35B 参数模型稳定达到 **63+ tokens/秒**,需要选择正确的计算后端、理解混合模型缓存的工作原理,并将容器化运行时调优至线程级别。
本指南将解析实现该性能级别背后的工程原理,详细介绍如何利用原生 SYCL 计算后端、使用上下文检查点系统,以及部署一个生产就绪的 Docker 栈。
> **硬件说明:** 本指南基于搭载 DDR4 内存的 i5-12400F 和 Intel Arc Pro B70 (32GB) 开发。所有基准测试均针对 llama.cpp 提交版本 `210a657` (https://github.com/ggml-org/llama.cpp/commit/210a6570ceda20c5d6439172c09ada08c3754cc9)。使用 `--n-gpu-layers 99` 和 `--cache-ram 0` 时,整个模型和 KV 缓存完全驻留在 VRAM 中——推理过程中 PCIe 总线仅传输 token ID 和 logits,因此主机内存带宽对 token 生成几乎无关紧要。这对于像 Qwen 3.6-35B-A3B 这样的稀疏 MoE 模型尤其如此,因为每个 token 仅激活 3B 参数。对于密集模型或需要溢出到主机 RAM 的配置,系统带宽会变得重要得多。
---
## 1. 计算架构:为什么 SYCL 胜过 Vulkan(在它起作用时)
在 Intel 硬件上配置像 `llama.cpp` 这样的计算引擎时,开发者通常会在两个后端之间选择:Vulkan 或 SYCL。对于 Intel 芯片上的计算密集型机器学习工作负载,**SYCL 通常能提供更高的峰值性能**——但差距在很大程度上取决于你的系统配置和驱动成熟度。
| 特性 | Vulkan 后端 | SYCL (oneAPI) 后端 |
|---|---|---|
| **抽象层** | 高级别、跨厂商图形 API | 低级别、Intel 原生计算运行时 |
| **硬件钩子** | 标准计算着色器 | 通过 Intel Level Zero 驱动直接执行 |
| **矩阵运算** | 通用计算着色器 | 可利用 **Xe 矩阵扩展 (XMX)** 进行提示处理 |
| **内存路径** | 标准主机到设备映射 | 统一共享内存 (USM),具有优化分配 |
| **Flash Attention** | 支持(软件实现) | 支持(软件实现,针对 Intel 优化) |
| **设置复杂度** | 低——大多数系统开箱即用 | 高——需要 oneAPI 工具包、特定驱动版本 |
| **稳定性** | 在消费级 Battlemage 卡上更成熟 | 可能对 PCIe 代数、RAM 速度和驱动版本敏感 |
Vulkan 旨在跨多个厂商提供广泛兼容性,因此它基于最低公分母模型运行。它无法原生利用 Intel 芯片中内置的硬件特定计算特性。
SYCL 由 Intel 的 oneAPI 工具包驱动,通过 Level Zero 驱动直接与硬件通信。这使得运行时可以将执行队列绑定到硬件中的 XMX 引擎,用于提示处理(GEMM 操作),从而在计算密集型工作负载上获得显著的吞吐量提升。对于 token 生成——这是内存带宽密集型而非计算密集型的——优势较小,但 USM 优化和立即命令列表调度仍能带来可测量的改进。
**需要注意:** SYCL 的优势并非普遍存在。一些用户报告称,在某些系统配置上,Vulkan 能提供更好或更一致的性能,尤其是在运行对主机内存带宽敏感的密集模型时。关于 SYCL 性能不佳的报告往往涉及较旧平台上的密集模型——对于像本例这样完全卸载的稀疏 MoE 工作负载,后端选择比总线更重要。话虽如此,在确定使用哪个后端之前,应在你的特定硬件上对两个后端进行基准测试。
---
## 2. 理解 llama.cpp 中的混合模型缓存
Qwen 3.6 是一种混合模型,融合了标准 Attention 层和循环/Gated-DeltaNet 层。这种架构会带来特定的缓存行为,理解这些行为对于优化多轮对话性能至关重要。
### 上下文检查点如何工作
在推理过程中,`llama.cpp` 会定期保存模型内部状态的快照——称为上下文检查点——这样同一对话中的后续轮次就不需要重新处理整个历史记录。当后续消息到达时,服务器会找到最新的有效检查点并从中恢复,只处理新的 token。
对于混合模型,这种检查点系统尤为重要,因为循环层维护着一个按顺序从每个先前 token 构建的运行时状态。你不能像对待标准 KV 缓存那样部分回滚循环状态。
### 同一对话中的多轮交互:快速
当继续一个持续中的对话时,检查点系统工作得很好。服务器会找到上一轮的检查点,恢复完整的模型状态(Attention KV 缓存 + 循环状态),并仅评估新的 token:
```
slot update_slots: id 0 | Checking checkpoint with [17, 17] against 21...
slot update_slots: id 0 | restored context checkpoint (pos_min = 17, n_past = 18, size = 62.813 MiB)
slot print_timing: id 0 | prompt eval time = 89.49 ms / 4 tokens
```
处理新 token 仅需 89 毫秒,因为整个之前的对话状态已从检查点恢复。
### 切换对话:需要完全重新处理
当服务器切换到 *不同* 的对话时,现有的检查点会失效——循环状态是从之前对话的 token 构建的,对于新对话在数学上是错误的。这是循环架构的一个基本属性,而非软件 bug:
```
slot update_slots: id 0 | Checking checkpoint with [17, 17] against 2...
slot update_slots: id 0 | forcing full prompt re-processing due to lack of cache data
(likely due to SWA or hybrid/recurrent memory)
```
服务器正确识别出没有检查点覆盖新对话的 token 序列,并从零开始重新处理。实际上,由于快速的 SYCL 提示处理管道(在 JIT 预热后为 330–530 t/s),这只需几百毫秒:
| 提示长度 | 重新处理时间 |
|---|---|
| 11 个 token | 180 毫秒 |
| 243 个 token | 740 毫秒 |
| 327 个 token | 691 毫秒 |
| 351 个 token | 657 毫秒 |
对于使用 `--parallel 1` 的单用户部署,对话切换是常态。重新处理成本与提示长度成线性关系,但相对于生成时间仍然很小。
### seq_rm 补丁(PR #22534 / Issue #22746)
存在一个影响混合模型的、独立的真正 bug:当上下文窗口填满且 `llama.cpp` 需要通过 `llama_memory_seq_rm` 截断旧 token 时,循环层无法执行部分序列删除。在未修补的构建中,此失败会触发破坏性的缓存刷新:
```
// 未修补行为:seq_rm 失败时
SLT_WRN(slot, "failed to truncate target tokens");
slot.prompt_clear(true);
slot.n_prompt_tokens_cache = 0;
```
社区修复方案会拦截此失败,认识到它是混合架构的预期限制,并保留有效的缓存状态而不是破坏它。此补丁包含在下面的 Dockerfile 中,对于推动上下文窗口限制的长对话最为重要。
### 首次请求时的 JIT 编译
另一个值得注意的行为:容器启动后的第一个请求包含一次性的 SYCL 内核 JIT 编译成本。在这套硬件上,大约需要 27 秒:
```
slot print_timing: id 0 | prompt eval time = 27384.24 ms / 22 tokens
```
这不是缓存问题——这是 SYCL 运行时在首次使用时编译 GPU 内核。无论检查点是否可用,每个后续请求都会全速运行。
---
## 3. 生活在主分支:双刃剑
要在消费级 Intel 芯片上实现 63+ tokens/秒,你不能依赖数月的稳定版本。你必须从最新的 `llama.cpp` 上游 `master` 分支直接编译。然而,将前沿版本视为生产环境会带来一系列明显的权衡。
### 优点:原始创新
通过直接从源码编译,你可以立即继承社区工程化的最新底层优化。对于 Intel 硬件,这意味着能立即获得:
- **上游 SYCL 重构:** 对统一共享内存 (USM) 如何跨 Intel 的 Level Zero 计算运行时路由数据的快速改进。
- **前沿架构支持:** 对像 Qwen 3.6 这样的新发布模型的复杂分词和层方案的原生支持。
- **微优化:** 立即集成编译器特定标志 (`icx`/`icpx`),减少 CPU 到 GPU 编排延迟。
### 缺点:未经验证的回归
这些优化尚未进入稳定版本的原因在于,它们并未在所有硬件组合上经过彻底验证。当你从 `master` 构建时,你不仅获得了最新特性——**你也继承了最新的 bug。**
混合模型的 `seq_rm` 问题就是一个很好的例子——它是一种只在特定条件下(混合架构上的长对话)才会出现的边缘情况,需要阅读源代码才能诊断。在替代芯片上部署本地 LLM 时,从源码编译是获得最大性能的必要条件,但**持续监控和针对性修补**是生活在前沿所必须付出的代价。
---
## 4. 生产部署蓝图
为了在不修改主机系统依赖项或创建冲突库版本的情况下部署此设置,整个执行环境被容器化。
### Dockerfile
这个多阶段 `Dockerfile` 使用 Intel 官方 `deep-learning-essentials` 容器作为基础。它拉取最新的 `llama.cpp` master 分支,通过 `awk` 脚本应用 `seq_rm` 混合内存补丁,并使用 Intel 的 `icx` 和 `icpx` 编译器编译运行时。
```
ARG ONEAPI_VERSION=2025.3.3-0-devel-ubuntu24.04
# ==========================================
# STAGE 1: Build & Patch Engine
# ==========================================
FROM intel/deep-learning-essentials:$ONEAPI_VERSION AS build
ARG LEVEL_ZERO_VERSION=1.28.2
ARG LEVEL_ZERO_UBUNTU_VERSION=u24.04
RUN apt-get update && apt-get install -y git libssl-dev wget ca-certificates gawk && \
cd /tmp && \
wget -q "https://github.com/oneapi-src/level-zero/releases/download/v${LEVEL_ZERO_VERSION}/level-zero_${LEVEL_ZERO_VERSION}%2B${LEVEL_ZERO_UBUNTU_VERSION}_amd64.deb" -O level-zero.deb && \
wget -q "https://github.com/oneapi-src/level-zero/releases/download/v${LEVEL_ZERO_VERSION}/level-zero-devel_${LEVEL_ZERO_VERSION}%2B${LEVEL_ZERO_UBUNTU_VERSION}_amd64.deb" -O level-zero-devel.deb && \
apt-get -o Dpkg::Options::="--force-overwrite" install -y ./level-zero.deb ./level-zero-devel.deb && \
rm -f /tmp/level-zero.deb /tmp/level-zero-devel.deb
WORKDIR /app
# Pin to tested commit — update this when upgrading
ARG LLAMA_CPP_COMMIT=210a6570ceda20c5d6439172c09ada08c3754cc9
RUN git clone https://github.com/ggml-org/llama.cpp.git . && \
git checkout $LLAMA_CPP_COMMIT
# Inject seq_rm Hybrid Memory Patch (PR #22534 / Issue #22746)
# Prevents destructive cache flush when seq_rm fails on hybrid/recurrent layers
RUN cat << 'EOF' > patch.awk
/common_context_seq_rm\(ctx_tgt, slot\.id, p0, -1\);/ {
print " if (!llama_memory_seq_rm(llama_get_memory(ctx_tgt), slot.id, p0, -1)) {"
print " if (ctx_tgt_seq_rm_type == COMMON_CONTEXT_SEQ_RM_TYPE_FULL && slot.n_prompt_tokens_cache > 0) {"
print " SLT_INF(slot, \"seq_rm failed (expected for hybrid) - keeping %d cached tokens from checkpoint\\n\", slot.n_prompt_tokens_cache);"
print " } else {"
print " SLT_WRN(slot, \"failed to truncate target tokens with position >= %d - clearing the memory\\n\", p0);"
print " slot.prompt_clear(true);"
print " slot.n_prompt_tokens_cache = 0;"
print " }"
print " }"
print ""
print " if (ctx_dft) {"
print " if (!llama_memory_seq_rm(llama_get_memory(ctx_dft.get()), slot.id, p0, -1)) {"
print " if (ctx_dft_seq_rm_type == COMMON_CONTEXT_SEQ_RM_TYPE_FULL && slot.n_prompt_tokens_cache > 0) {"
print " SLT_INF(slot, \"draft seq_rm failed (expected for hybrid) - keeping %d cached tokens\\n\", slot.n_prompt_tokens_cache);"
print " } else {"
print " SLT_WRN(slot, \"failed to truncate draft tokens with position >= %d - clearing memory\\n\", p0);"
print " }"
print " }"
print " }"
getline; getline; getline
next
}
{ print }
EOF
RUN awk -f patch.awk tools/server/server-context.cpp > tmp.cpp && mv tmp.cpp tools/server/server-context.cpp
# Compile using native Intel OneAPI Compilers
RUN cmake -B build -DGGML_NATIVE=OFF -DGGML_SYCL=ON -DCMAKE_C_COMPILER=icx -DCMAKE_CXX_COMPILER=icpx -DGGML_BACKEND_DL=ON -DGGML_CPU_ALL_VARIANTS=ON -DLLAMA_BUILD_TESTS=OFF && \
cmake --build build --config Release -j$(nproc)
RUN mkdir -p /app/lib && find build -name "*.so*" -exec cp -P {} /app/lib \;
RUN mkdir -p /app/full && cp build/bin/* /app/full
# ==========================================
# STAGE 2: Runtime Environment Setup
# ==========================================
FROM intel/deep-learning-essentials:$ONEAPI_VERSION AS base
ARG IGC_VERSION=v2.20.5
ARG COMPUTE_RUNTIME_VERSION=25.40.35563.10
RUN mkdir /tmp/neo/ && cd /tmp/neo/ \
&& wget https://github.com/intel/intel-graphics-compiler/releases/download/$IGC_VERSION/intel-igc-core-2_2.20.5+19972_amd64.deb \
&& wget https://github.com/intel/intel-graphics-compiler/releases/download/$IGC_VERSION/intel-igc-opencl-2_2.20.5+19972_amd64.deb \
&& wget https://github.com/intel/compute-runtime/releases/download/$COMPUTE_RUNTIME_VERSION/intel-ocloc_25.40.35563.10-0_amd64.deb \
&& wget https://github.com/intel/compute-runtime/releases/download/$COMPUTE_RUNTIME_VERSION/intel-opencl-icd_25.40.35563.10-0_amd64.deb \
&& wget https://github.com/intel/compute-runtime/releases/download/$COMPUTE_RUNTIME_VERSION/libigdgmm12_22.8.2_amd64.deb \
&& wget https://github.com/intel/compute-runtime/releases/download/$COMPUTE_RUNTIME_VERSION/libze-intel-gpu1_25.40.35563.10-0_amd64.deb \
&& dpkg --install *.deb && apt-get update && apt-get install -y libgomp1 curl && apt clean -y && rm -rf /tmp/neo
# ==========================================
# STAGE 3: Final Server Image
# ==========================================
FROM base AS server
ENV LLAMA_ARG_HOST=0.0.0.0
COPY --from=build /app/lib/ /app
COPY --from=build /app/full/llama-server /app
WORKDIR /app
ENTRYPOINT [ "/app/llama-server" ]
```
### `docker-compose.yaml`
此配置通过最小化硬件虚拟化和操作系统调度器干扰来优化性能。
``
###############################################################################
# llama.cpp SYCL – Qwen3.6-35B-A3B on Intel Arc Pro B70
# - Includes PR #22534 (seq_rm Hybrid Memory Patch)
# - CPU Pinned to Core 4, Threads locked to 1 (Zero Contention)
# -
相似文章
在 12GB 显存下,使用 Qwen3.6 35B A3B 与 llama.cpp MTP 实现 80 tok/sec 的速度和 128K 上下文
一名用户分享了一份配置方案,该方案在使用 llama.cpp 和多令牌预测(MTP)的情况下,能在 12GB 显存的 GPU 上让 Qwen3.6 35B A3B 模型实现超过每秒 80 个令牌的生成速度。帖子中包含了基准测试结果以及用于优化性能的具体命令行参数。
在 8GB 显存和 32GB 内存上运行 Qwen3.6 35b a3b,~190k 上下文
作者分享了一种高性能的本地推理配置,使用支持 TurboQuant 的修改版 llama.cpp,在硬件受限(8GB 显存、32GB 内存)的情况下运行 Qwen3.6 35B A3B,实现了 ~37-51 tok/sec 的生成速度,并支持 ~190k 上下文。
QWEN3.6 + ik_llama 快得离谱
用户报告成功部署 Qwen 3.6 与 ik_llama 量化,在消费级硬件(16GB VRAM、32GB RAM)上实现 200k 上下文窗口下 50+ token/秒。
Intel Arc Pro B70 llama.cpp 基准测试结果公布
Intel Arc Pro B70 GPU 在 Qwen 模型上通过 SYCL 运行 llama.cpp 的基准测试结果显示每秒 63 个 token 的性能。
在24GB显存环境中运行Qwen 3.6 27B的配置:后端对比、量化选择与设置(llama.cpp, ik_llama.cpp, BeeLlama, vllm)
本文对比了在RTX 3090 24GB上运行Qwen 3.6 27B使用的llama.cpp后端,发现搭配IQ4_KS量化的ik_llama.cpp性能最佳(预填充1261 tok/s,解码72.9 tok/s)。