计算后端

TensorAgent 另提供 iOS/iPadOS 应用目标,在真机 Apple 设备上使用 GGML Metal 后端;构建时启用 TensorSharpIosTargets=true

后端通过 --backend(CLI 与服务器)选择,决定由哪种硬件运行数学运算。对任何未实现的运算,每个后端都会回退到 CPU,因此各处输出都保持正确 —— 后端只在速度上不同。TensorAgent 另提供 iOS/iPadOS 应用目标,在真机 Apple 设备上使用 GGML Metal 后端。

我该用哪个后端?

你的硬件推荐参数说明
Apple Silicon(Mac)GGML Metalggml_metalmacOS 默认。mlx 是另一条 Apple Silicon GPU 路径。
iPhone / iPadTensorAgent 的 GGML Metalggml_metal原生 iOS 目标;使用 TensorSharpIosTargets=true 构建 TensorAgent。不使用 ASP.NET Core host,也不启动子进程。
Windows / Linux + NVIDIAGGML CUDAggml_cuda测试最充分的 NVIDIA 路径。cuda 是用于实验的直接 PTX/cuBLAS 后端 —— 也是支持多 GPU 张量并行的那一个。
Windows / Linux + AMD / Intel / NVIDIAGGML Vulkanggml_vulkan与厂商无关的 GPU 路径(ggml-vulkan,驱动支持时启用 cooperative-matrix 加速)。当主机存在 Vulkan 运行时时会在原生构建时自动启用;用 --no-vulkan 退出。
无 GPU / 可移植 / 调试纯 C# CPUcpu无原生依赖,且托管 matmul 现在跑在多核工作线程池上。要更快的 CPU 推理请用 ggml_cpu(原生内核)。

所有后端详解

后端参数最适合
直接 CUDA / cuBLAScudaNVIDIA 推理与实验
MLX MetalmlxApple Silicon(GGML Metal 的替代)
GGML Metalggml_metalApple Silicon(macOS 默认)
GGML CUDAggml_cuda通过 ggml 的 NVIDIA 推理
GGML Vulkanggml_vulkan通过 ggml 的厂商无关 GPU 推理(AMD / Intel / NVIDIA)
GGML CPUggml_cpu原生 CPU 内核
纯 C# CPUcpu可移植与调试
iOS GGML MetalTensorAgent 应用通过静态链接的 iOS .xcframework 在 iPhone/iPad 上本地推理

GGML Metal、GGML CUDA 与 GGML Vulkan

建立在链接 ggml 的原生 C++ 桥接之上的 GPU 路径。

三者都在不反量化为 FP32 的情况下运行原生量化 matmul(Q4_K_M、Q8_0 ……),并配有驱动 ggml_flash_attn_ext 的原生分页注意力内核。

多 GPU。GGML CUDA 与 GGML Vulkan 同样支持张量并行--tp N 让每个 rank 在自己的 GPU 上拥有独立的 ggml 后端、权重分片与 KV 缓存,由 rank 工作线程池并发驱动,跨 GPU AllReduce 走 ggml 的集合通信(构建时能找到 NCCL 就用 NCCL),小载荷则在主机内存中归约。融合的按 rank block 计算图让 Gemma 4 上 --tp 2 的 decode 快于单卡,也让超出单卡显存的模型能够完整跑在 GPU 上。它们同样承载另一种多 GPU 模式 —— 按层切分:每张 GPU 拿整层、没有集合通信、不切分任何权重;DeepSeek V4 Flash 与 GLM 5.x 不传 --tp 时自动如此,Qwen 3.8 Flash Next 上的 --tp N 跑的也是它,换来的是容量而非速度。GLM-5.2、GLM-5.3 与 GLM-5.3-Flash 传入 --tp N 时都会选择仅支持本地单进程的原生张量并行 —— 每个 rank 都复制一份 MLA 与 indexer 缓存,因此 KV 占用按 rank 倍增、能装下的上下文变短;GLM-5.3 上的推测解码只在默认按层切分时生效,它在多于一个 rank 时也从未实测过,而跨节点的 --tp-node-id / --tp-peers 对整个家族都会在构建模型之前被拒绝。→ TP 与按层切分

已验证的 E4B 快路径:仓库基准已在这些 GGML 原生 GPU 后端上验证 Gemma 4 E4B Q8_0 家族。E4B 的 PLE 与共享 KV 布局会进入融合整模型 prefill/verify 和单图 decode 路径,服务端 N=1 时也会自动选择融合路径。请从 Gemma 4 E4B 指南开始。

MLX Metal

--backend mlx 是建立在 mlx-c 之上、GPU 加速的 Apple Silicon 路径。它在不反量化为 FP32 的情况下实现量化运算(Q4_K_M、Q8_0、Q5_K、Q6_K、IQ2_XXS、IQ4_XS、IQ4_NL、MXFP4 ……)、融合的 decode/prefill Metal 内核、编译图内核、异步 worker 调度、批量 MoE decode 与 MoE 专家卸载。它通过 mlock(2) 把 GGUF mmap 钉在物理内存中,并根据主机的统一内存容量推导分配器上限。需要 libmlxc(本地构建,或通过 TENSORSHARP_MLX_LIBRARY / TENSORSHARP_MLX_LIBRARY_DIR 定位)。

直接 CUDA

--backend cuda 是一条纯 C# 路径,使用 CUDA Driver API、cuBLAS GEMM,以及常见 float32 运算的 PTX 内核(fill、一元/二元/三元、激活、RMSNorm、softmax、RoPE/RoPEEx、SDPA、GQA prefill/decode、因果掩码、gather/concat),外加受支持量化类型的原生量化 matmul/get-rows。未支持的运算在保留张量语义的前提下走 CPU 回退。它也是MTP 推测解码能获益的纯 C# 后端。

它支持张量并行--tp N 把一个模型切分到 N 张 CUDA GPU 上,--tp-node-id / --tp-peers 则把并行组扩展到多台机器。GGML CUDA 与 GGML Vulkan 后端同样支持切分(见上文);不过在走整模型执行器的架构上 --tp 是另一层含义 —— 在 Qwen 3.8 Flash Next 上是按层切分,在 DeepSeek V4 Flash 上只是设备数上限,在 GLM-5.2、GLM-5.3 与 GLM-5.3-Flash 上,则会由 GGML GPU 后端启用仅支持本地单进程的原生 TP。MLX 与 CPU 后端为单设备 —— 在多 GPU 主机上它们只会选择其中一张卡(不带 --tp 时 Vulkan 用 --gpu-device),而不会切分模型。→ 多 GPU 与多节点

CPU 后端

纯 C# 路径现在是多核的。托管 matmul 不再走每次 matmul 一遍的 Parallel.For,而是交给常驻的自旋-再挂起工作线程池(TensorSharp.Models/CpuWorkerPool.cs),工作项也按实际工作量而非线程数切分。在 gemma-4-E4B-it-Q8_0、122 CPU 配额下,通过 TS_CPU_POOL 在同一个二进制里交替 A/B 实测:相比旧路径 prefill 约 +15%、decode 约 2.8×。线程池刻意不占满所有核心 —— 默认宽度是可用 CPU 的一半,因为自旋的 worker 会饿死仍在用 ThreadPool 的其余 CPU 路径(占满全部核心时同一模型反而更慢)。可用 TS_CPU_THREADSTS_CPU_POOLTS_CPU_SPINTS_CPU_TASK_BYTESTS_CPU_TASKS_PER_WORKER 调节。

量化权重现在在这里也是零拷贝映射的。cpu 是最后一个在加载时把每个量化张量复制进新的匿名内存、而不是直接绑定 GGUF 映射的后端 —— GGML 后端一直都是后者。加载器现在会打印它实际拿到的拆分,例如 Quantized: 103255 MB (103255 MB file-backed), F32: 983 MB。任何此前需要复制权重的模型都会加载得更快,越大的量化检查点收益越明显:GLM-5.3-Flash UD-Q2_K_XL 从一次永远无法完成的加载(常驻集 412 GB 且仍在增长)变成约 48 s,其中大部分是页缓存预取。

托管 i-quant 的覆盖面更宽了。IQ2_XSIQ4_XS 有了托管反量化实现(与 ggml 自己的 dequantize_row_* 对拍过),并进入了 CPU 量化存储矩阵,因此它们在加载时会保持量化,而不再展开成 F32 —— 正是这种展开让 GLM-5.3-Flash 的 2-bit 检查点要求 765 GB。IQ2_XS × Q8_KIQ3_XXS × Q8_K 也有了直接点积内核以及 AVX2 路径(VecDotIq2XsQ8KAvx2VecDotIq3XxsQ8KAvx2),不必再回落到通用的「反量化整行到暂存区」路径。这两项改动对 --backend cpu 上的任何模型都生效。如果你要再加一个这样的内核,请注意:ggml 是把一个常数折叠进某些 i-quant 点积的结果里(IQ2_XS 为 0.125,IQ3_XXS 为 0.25,IQ3_S 为 1.0),而不是折进每个块的 scale —— 漏掉它就是 8× 的误差,而且不会崩溃,只会产出看起来通顺的垃圾。

每个模型家族都有纯 C# 路径。MiniMax-H3 是最后一个没有的:它此前每个阶段都是无条件的原生 ggml 调用,而现在 --backend cpu 上的 t2v、i2v、fl2v 与参考条件(静图、片段、音轨)都走托管的 DiT、文本编码器、VAE 与视觉塔。输出与 GGML 路径贴合得很紧 —— t2v、i2v 与 ref-audio 的吻合程度甚至高于 GGML 自家 flash 内核与其显式 softmax 回退之间的吻合程度 —— 唯一的例外是视觉塔,其残差比该对照更大,原因尚未查明。速度是有得有失、并非一律更慢:同一段片段上,i2v 为 70 s、ggml_cpu 为 176 s,ref-audio 为 63 s 对 71 s;但 t2v 为 69 s 对 14 s、ref-image 为 112 s 对 20 s —— 视觉塔是托管路径里最贵的一段。→ MiniMax-H3

非 ggml 的各家族共用同一套原语:Direct{Context,Linear,Ops}TensorSharp.Models/Direct/DirectOps.cs)同时支撑 Wan 视频网络与 MiniMax-H3 的 cpucuda 路径,其按行循环也走同一个工作线程池。在 CPU 上 DirectLinear 不再于加载时把量化权重展开成 F32,而是保留 GGUF 的存储类型直接参与乘法:一段小型 Wan 渲染(256×160、5 帧、1 步、--backend cpu)实测 80.9 s 对 121.4 s,权重内存少 4×,而且相对原生 ggml_cpu 渲染反而略更接近(43.51 dB 对 43.39 dB)。F16/BF16/F32 权重仍走普通 GEMM;TS_DIRECT_QUANT_WEIGHTS=0 可恢复旧行为。

DeepSeek V4.1 也有一套托管的整模型执行器。deepseek41--backend cpu 上运行纯 C# 的 DeepSeek4CpuExecutor —— ratio-1 与 ratio-2 的 block 压缩器、带 lightning indexer top-k 的共享压缩缓存与 indexer 缓存、候选 block 剪枝、Engram 行 gather、延迟超连接门控,以及检查点自带的训练态缓存量化(原始行 FP8 E4M3、indexer MXFP4、压缩缓存 NVFP4)—— 不用 ggml、不用原生库,也不用 GPU。它以独立的 PyTorch oracle eng/dsv41-reference.py 为准绳,按 atol=rtol=2e-5 加贪心 argmax 完全一致来把关,但对拍用的是一个五层的 F32 夹具模型,而不是真实权重,而且不设 TS_DSV41_FIXTURE_DIR 指向夹具目录时这些测试会静默返回。它仍然需要 deepseek41.engram.bin 边车文件,也没有视觉能力 —— 视觉伴随模型是原生 ggml 组件,LoadVisionEncoder 在这个后端上会抛异常 —— 而 TS_DSV4_THREADS 在这里默认取 ProcessorCount,而不是别处的 min(核心数, 32)。和 cuda 一样,它是正确性与可移植性通道,而非服务通道:完整 V4.1 检查点在它上面的吞吐、加载时间与常驻内存都没有实测数据,本节上面那些 TS_CPU_THREADS / TS_CPU_SPIN 数字也不属于 V4.1。→ deepseek41 的后端

DeepSeek V4、V4.1 与 GLM 5.x:专属整模型执行器

有三个架构不走通用的逐算子前向。deepseek4 那套 284B 的压缩稀疏注意力 MoE 结构由三套专属的整模型执行器之一运行:

三者都会把权重按层切分到所有可见 GPU(CPU 那套则从映射分片流式读取),因此远大于单卡显存的模型依然跑得起来;--tp NTS_DSV4_NGPU 用来限制使用几张卡。投机解码(DSpark)可用于两个 GPU 引擎。→ DeepSeek V4 的下载与命令

deepseek41 的服务后端只有一个。DeepSeek V4.1 的原生计算图在 --backend ggml_cuda 下运行,这是它的服务路径——只有这条路径为它的融合算子提供了内核。ggml_cpu 用这些算子回退到的标量实现加载同一套图,目的是在没有 GPU 时验证架构,而不是拿来对外服务:这个检查点每层每个 token 要从 246 GiB 中读出 384 个路由专家中的 6 个。TS_DSV41_ALLOW_NON_CUDA_GPU=1 可额外允许 ggml_vulkanggml_metal:普通计算图仍跑在 GPU 上,只有架构专属算子落到 CPU 后端,每次都要一次主机往返——需要显式开启,因为那道拒绝原本挡住的是静默回退。cpu 运行纯 C# 的 V4.1 执行器,已按 2e-5 对齐 PyTorch 参考实现;cuda 用 Direct CUDA 引擎自己的内核运行 V4.1(不经过 ggml),但尚无数值门禁。两者都是正确性与可移植性通道,而非服务通道。mlx 会在读取权重之前就失败,而不会把 V4.1 的权重塞进并未实现它的图。在 GPU 上,架构专属算子以 GGML_OP_CUSTOM 节点发出,由一个 TensorSharp 后端执行——它包裹所在 GPU 的 CUDA 后端并在调度器中取而代之,因此普通节点仍以图视图的形式在同一条流上交给 CUDA。→ DeepSeek V4.1

glm-dsa 那套 744B-A40B 的 MLA + DeepSeek 稀疏注意力结构同样如此,只不过是两套:

GLM-5.3(非 Flash)与 GLM-5.2 是同一套 glm-dsa 的 block 结构 —— 79 个 block(78 层主干 + 一个 NextN)、256 个路由专家 top-8 外加一个共享专家、带 lightning indexer 的 MLA、rope base 8e6 —— 因此它直接走上面这条路径,不需要新代码,也不需要新开关:原生整模型执行器覆盖 ggml_cuda / ggml_vulkan / ggml_cpu / ggml_metal,托管逐算子路径覆盖 cpu / cuda,在 GGML 后端上设 TS_GLM_NATIVE=0 也会切到它;MLX 同样不支持它。它在两重意义上都是仅文本的:unsloth/GLM-5.3-GGUF 在任何量化下都没有发布 mmproj,而 LoadVisionEncoderglm-dsa 上遇到 --mmproj 只会告警并忽略它,所以多传这个开关得到的是一次纯文本运行,而不是一次失败。--tp N 在这里同样被接受为仅支持本地单进程的原生张量并行,MLA 与 indexer 缓存按 rank 复制 —— --tp-node-id / --tp-peers 对整个 GLM 家族都会在构建模型之前被拒绝 —— 而 GLM-5.3 在多于一个 rank 时从未实测过。--spec 在默认按层切分(不传 --tp)时生效,从 blk.78 那个完整的 NextN block 出草稿:该 block 没有自带 nextn.shared_head_head.weight,只能借用主干的 LM head,而这个 head 在 --tp 下是按列切分的,加载器不会拿某个 rank 手里的那一条词表切片去出草稿。8× A40 46 GB(无 NVLink)上的实测(UD-Q2_K_XL、7 个分片、236.4 GiB、10,531 token 提示、300 个 decode token、3 次取中位数、整层放置):prefill 251.6 t/s、decode 20.48 t/s,llama.cpp 的 decode 为 20.28 t/s;加载 264 s 对 753 s —— decode 打平,加载快 2.9×,TTFT 则是诚实的短板:41.9 s 对 29.0 s(约慢 1.4×)。

GLM-5.3-Flashglm5next)走的是同一个原生执行器与同一个 GlmDsaModel,不传 --tp 时仍使用相同的默认按层切分;在 GGML GPU 后端上,--tp N 会选择仅支持本地单进程的原生张量并行。满足条件的完整切分配置使用并发的按 rank 分段计算图;CPU MoE、tracing、部分切分、超额 rank、缺少原生超连接内核或 TS_GLM_TP_FUSED=0 会选择组合调度器回退。NextN/MTP 推测解码目前也尚未为它实现。

GLM-5.3-Flash 也能跑在 --backend cpu —— 同一条托管逐算子路径,零原生依赖。它在文本上可用,但应当被当作用于对拍的参考实现,而不是快路径:同一份文件上,prefill 约比 ggml_cpu 慢 5.7×、decode 约慢 2.4×;其 prefill logits 与 ggml_cpu 的余弦相似度为 0.9567(词表宽 154880)—— 已经很接近,但这并等于已经证明逐位一致,当排名前两位的 logit 接近打平时,贪心解码出的文本就会分叉。TS_DUMP_LOGITS 会把第一次真实前向的 logits 写到文件里,正是为这种对比准备的。细节见仓库中的 docs/models/glm_zh-cn.md 卡片。

由于 1M token 的 MLA 缓存约 93 GiB,自报的上下文被当作上限处理:权重落盘后,加载器会测量实际空闲的显存并按能装下的大小定上下文,同时打印它的选择(3 卡按层切分为 342,272 token)。设 MAX_CONTEXT 则把某个长度变成硬性要求。参见 GLM 5.x

🔎

服务器会在 GET /api/modelssupportedBackends)中报告主机上实际可用的后端。如果缺少 CUDA 或 MLX 后端,说明主机在启动时未检测到可用的驱动 / 运行时。如果缺少 ggml_vulkan,说明原生桥接库未启用 Vulkan 构建,或未找到支持 Vulkan 1.3 的设备/驱动。

构建原生库

请先按平台安装并验证 .NET 10 SDK。原生 GGML 库会在首次 dotnet build 时自动构建。要手动构建或启用 CUDA:

cd TensorSharp.GGML.Native

# macOS(Metal)
bash build-macos.sh

# Linux —— 仅 CPU、强制启用 CUDA、强制关闭 Vulkan
bash build-linux.sh
bash build-linux.sh --cuda
bash build-linux.sh --no-vulkan
# Windows —— 仅 CPU、强制启用 CUDA、强制关闭 Vulkan
.\build-windows.ps1 --no-cuda
.\build-windows.ps1 --cuda
.\build-windows.ps1 --no-vulkan

GGML Vulkan 后端会自动启用——当主机存在 Vulkan 运行时(Windows 上的 vulkan-1.dll 或 Linux 上的 libvulkan.so.1 loader,近期 GPU 驱动均自带)时即启用;用 --no-vulkan(或 TENSORSHARP_GGML_NATIVE_ENABLE_VULKAN=OFF)退出,且显式选择会经 CMake 缓存在重复构建中保持。Windows 上已安装 LunarG Vulkan SDK 时直接使用;未安装时构建会通过 eng/fetch-vulkan-toolchain.ps1 自动把便携工具链(Vulkan-Headers、由系统 loader 生成的 vulkan-1 导入库、glslc、SPIRV-Headers)准备到 ExternalProjects/vulkan-toolchain/。Linux 上有发行版开发包时直接使用(apt install libvulkan-dev glslc spirv-headers);否则构建会通过 eng/fetch-vulkan-toolchain.sh 自动补齐缺失部分(Vulkan-Headers、来自 shaderc CI 预编译的 glslc、SPIRV-Headers)。运行时需要支持 Vulkan 1.3 的 GPU 驱动。

在 Windows/Linux 上,脚本会自动检测可见 NVIDIA GPU 的计算能力,并传入一个收窄的 CMAKE_CUDA_ARCHITECTURES(例如 RTX 3080 上的 86-real),从而大幅缩短 CUDA 构建时间。可显式覆盖:

TENSORSHARP_GGML_NATIVE_CUDA_ARCHITECTURES='86-real;89-real' bash build-linux.sh --cuda
bash build-linux.sh --cuda --cuda-arch='86-real;89-real'

你也可以直接在 dotnet build 中请求 CUDA:

TENSORSHARP_GGML_NATIVE_ENABLE_CUDA=ON dotnet build TensorSharp.Cli/TensorSharp.Cli.csproj -c Release

MLX 原生库(仅 macOS)

MLX 后端依赖 libmlxc。一个辅助脚本会获取并构建它:

bash TensorSharp.Backends.MLX/build-native-macos.sh

它会把库写入 TensorSharp.Backends.MLX/Native/dist/。运行时后端会先探测应用目录;用 TENSORSHARP_MLX_LIBRARYTENSORSHARP_MLX_LIBRARY_DIR 指向别处。

平台二进制发行状态

两个应用均按以下平台/后端矩阵发布:

归档捆绑的原生后端格式
win-x64-cpuGGML CPU.zip
win-x64-cudaGGML CUDA + 纯 C# CUDA (PTX) + CUDA 12.x 运行时.zip
linux-x64-cpuGGML CPU.tar.gz
linux-x64-cudaGGML CUDA + 纯 C# CUDA (PTX) + CUDA 12.x 运行时.tar.gz
osx-arm64GGML Metal + MLX.tar.gz

每个发行版的每个后缀都有 CLI 与 Server 两个文件。-cuda 归档仍需 NVIDIA GPU 与兼容驱动;macOS 归档需要 Apple Silicon。仍可选择从源码构建。