Windows 原生编译 SGLang(2/8):三铁证判定可行 + --no-deps 外科手术式安装

发布时间:2026/8/2 2:23:11

Windows 原生编译 SGLang(2/8):三铁证判定可行 + --no-deps 外科手术式安装 Windows 原生编译 SGLang2/8三铁证判定可行 --no-deps 外科手术式安装上一篇(第 0 篇)我们用 EPGF 架构把 Unlimited-OCR 部署到了能跑通推理的状态,并停在了一个岔路口:高性能后端 sglang 在 Windows 上装不了,主流建议是退回 Transformers 或转投 WSL2 / Docker。本篇先把结论摆在最前面——我们没有退,而是在原生 Windows 上从源码把它编出来了——然后讲清楚这件事到底难在哪、我们怎么组织这场攻关,以及为什么官方没有 Windows 包和Windows 编译不出来根本是两回事。一、先看结果:铁证三连技术复盘最怕我觉得我成功了。所以先把三条最硬的证据摆出来,每一条都可独立复验。证据一:产出了 Windows 平台的win_amd64wheel。K:\PythonProjects5\Unlimited-OCR\sglang\sgl-kernel\dist\sglang_kernel-0.4.3-cp310-abi3-win_amd64.whl注意结尾的win_amd64——这正是官方没有提供的那个平台标签。abi3表示它使用 Python 稳定 ABI(Limited API),一次编译可跨多个 Python 版本通用,cp310是其声明的最低兼容版本,实测在 Python 3.12 上安装、导入均正常。证据二:编译出来的 C/CUDA 扩展能被 Python 真正加载。python -c import sgl_kernel; print(Kernel OK)Kernel OK这一条比wheel 文件存在更关键:编译成功和运行可用是两件事。import sgl_kernel通过,说明我们逐行抠出来的那个二进制扩展,不只是编出来了,而是能加载进来。证据三:它正确填进了 sglang 主包的依赖链。pip show sglang sglang-kernel关键输出:sglang-kernel 0.4.3的Required-by: sglang,而sglang 0.0.0.dev11416g92e8bb79e主包的Requires:列表里明确列着sglang-kernel。也就是说,我们编出来的子包不是一个孤立的产物,而是严丝合缝地补进了主包本来就需要、但 Windows 上一直缺失的那个位置。python -c import sglang; print(sglang, sglang.__version__)sglang 0.0.0.dev11416g92e8bb79e三条证据合起来,坐实一件事:sglang 高性能后端,在原生 Windows 上,跑起来了。二、“官方没有 Windows 包” ≠ “Windows 编译不出来”这是整个系列最想先纠正的一个认知。当你在 Windows 上尝试安装 sglang(无论是项目自带的主包 wheel,还是从公网拉官方包)失败时,真实发生的是这样一条链:sglang 主包本身是纯 Python 的py3-none-any,看起来跟平台无关;但它在依赖里钉死了一个 C/CUDA 扩展子包sglang-kernel;官方只为这个子包发布了 Linux 预编译包(manylinux2014_x86_64),没有发布win_amd64;于是 pip 解析依赖时,在 Windows 上找不到任何能装的sglang-kernel,整条安装链断在这里。主流结论由此而来:“Windows 装不了 sglang。” 这个结论的前半句是事实(直接装确实会失败),但后半句的引申是错的——它把官方没有发布 Windows 预编译包偷换成了Windows 无法运行 sglang,再进一步引申成只能退 WSL2 / Docker。真相是:sglang-kernel是开源的,源码就在 sglang 仓库的sgl-kernel/目录下。官方没编 Windows 版,不代表它编不出来,只代表没有人(在官方流程里)去编它。缺的不是可能性,而是一份win_amd64的 wheel——那就自己把它编出来。这就是本系列的全部主张:不退 WSL、不上 Docker,在原生 Windows MSVC CUDA 工具链下,从源码编译出缺失的sglang-kernel,再让主包正常用上它。三、关键转折:为什么必须--no-deps把子包编出来之后,还有一个容易被忽略、但极具代表性的转折,值得在总览里先讲清楚——因为它精确地解释了为什么常规安装必然失败,而我们的路线能成。先说清楚这里的主包从哪来:Unlimited-OCR 项目仓库自带了一个wheel/目录,里面就放着 sglang 主包的 wheel(sglang-0.0.0.dev11416g92e8bb79e-py3-none-any.whl)。我们要装的就是这个项目自带的主包,而不是从公网 PyPI 拉官方包。但光装它不算完整——它依赖sglang-kernel子包,而这个子包正是 Windows 上一直缺、需要我们自己编出来的那个。于是正确的安装顺序是:先装我们编好的sglang-kernel 0.4.3(win_amd64),再装项目自带的主包 wheel。可即便子包已经装好,直接装主包仍然会报错:ERROR: Could not find a version that satisfies the requirement sglang-kernel0.4.1 (from sglang) ERROR: No matching distribution found for sglang-kernel0.4.1原因是:项目自带的这个主包 wheel,其元数据里把依赖精确钉死在了sglang-kernel0.4.1,而我们从当前源码编出来的是0.4.3。pip 按这个元数据严格解析时,会去公网找0.4.1的win_amd64包——它当然不存在,于是失败。这恰恰印证了按它声明的依赖正常装必然失败这个判断本身没错。但结论不该是装不了,而该是:绕过这次依赖解析,用已经在本地、已经装好的子包去满足它。pip install K:\...\wheel\sglang-0.0.0.dev11416g92e8bb79e-py3-none-any.whl --no-deps--no-deps告诉 pip:“别去重新解析依赖,我自己已经把该装的都装好了。” 主包随即安装成功。这一步是整条路线的临门一脚:前面所有的源码攻关,都是为了让这一步有底气说出我自己装好了。一个工程提醒:0.4.1与0.4.3的版本号差异,源于项目自带的主包 wheel 元数据里钉的是0.4.1,而当前源码 tag 编出来的子包是0.4.3,两者对不上。在自己复现时,以实际编出来的子包版本为准,用--no-deps装主包即可,不必纠结这个号差。完整依赖安装顺序:把--no-deps说得底气十足的前提,是真的把所有依赖都提前装好。这场攻关实际上有四个包需要严格按序安装:flashinfer-windows(flashinfer_python-0.6.11.post3-py3-none-any.whl):sglang 的 attention 计算后端依赖,Windows 官方不提供,需使用单独维护的 Windows 兼容 fork 手动编译出 wheel。py3-none-any后缀意味着它的 CUDA kernel 代码是 JIT 模式(运行时即时编译),wheel 本身是纯 Python 外壳——但它必须先于 sglang 主包安装,否则 sglang 运行时找不到这个后端。sgl-kernel(sglang_kernel-0.4.3-cp310-abi3-win_amd64.whl):本系列整场攻关的产物,紧接着装。kernels0.11.7:sglang 依赖链里一个未被自动拉取的缺失包。PyPI 上有预编译版本,uv pip install --link-modecopy kernels0.11.7装它只需几秒钟。sglang 主包(--no-deps):前三步都就位后,用--no-deps跳过依赖解析,让已装好的包顶上。步骤①②③必须在步骤④之前完成。flashinfer-windows 的编译过程本身也是一段独立的攻关(另见仓库flashinfer-windows/目录);kernels 包虽然装起来一瞬间,但它是 sglang 运行时不可缺少的一环——少了它,主包导入会在运行时才报缺依赖。四、这场攻关到底难在哪:全局地图把一个为 Linux GCC 精心打磨的 CUDA 扩展,搬到 Windows MSVC 上原生编译,难点不是某一个 bug,而是成体系的、跨越多个层次的不兼容。整个系列后续四篇,正是按这张地图的层次来组织的:前置·FlashInfer 依赖编译(第 2 篇)。在碰 sgl-kernel 之前,还有一个独立的前置关卡:sglang 的 attention backendFlashInfer官方不支持 Windows,必须手动编译SystemPanic/flashinfer-windowsfork 产出 wheel。这一关涉及 CUDA 13.x 头文件的对齐补丁(需管理员权限)、vswhere多版本 VS 选错、--force-reinstall悄悄把 CUDA torch 换成 CPU-only 版等独立踩坑。第一层·环境(第 3 篇)。还没碰到一行源码,光是把正确的编译器 正确的 CUDA 正确的生成器凑齐并稳定下来,就有一连串坑:机器上同时装着 VS2022 和 VS18 预览版、五个版本的 CUDA 并存、uv 创建的 venv 会重置 PATH、CMake 的生成器选择会被缓存写死……这些与 sglang 本身无关,却是所有人在 Windows 上编 CUDA 扩展都会撞上的共性问题。第二层·源码方言(第 4 篇)。数十个.cu/.cuh/.h源文件里,散布着大量GCC/Clang 能过、MSVC 不认的写法:编译器内建函数(__builtin_clz)、GNU 内联汇编拼法(__asm__/__volatile__)、GCC 属性(__attribute__((always_inline/weak)))、类型简写(uint/ushort)、非标准数学宏(M_LOG2E/M_LN2),以及 MSVC 预处理器的严格性(宏参数里的裸#指令、__VA_ARGS__的跨层展开)。第三层·MSVC 的语义严格性(第 5 篇)。比方言更深的一类:MSVC 在常量求值、重载决议上比 GCC 严格得多。std::numeric_limits::max()在设备端不被当作编译期常量、const被误当constexpr、嵌套 lambda 里的常量推导链断裂、half/bf16 构造函数的重载歧义、乃至 Windows SDK 一个古董宏#define small char对正常变量名的污染——直到 MSVC 自己在某个 CUTLASS 模板上内部崩溃(C1001)。第四层·架构裁剪与链接收尾(第 6 篇)。这一层是贯穿全程的主线决策,而非逐个 bug:sgl-kernel为 Hopper/Blackwell(SM90/SM100)等数据中心级架构硬编码了大量代码路径,而我们的目标是消费级的 RTX 3090(sm_86)——用不上的,就一律不编。裁剪干净之后,还要处理算子注册与实现不同步导致的链接期未解析符号(LNK2019),才能走完最后的链接、产出 wheel。这四层叠加,构成了一条此前几乎没有公开成功先例的路径。它确实很长——但每一层的每一个坎,都有明确的成因和可复现的解法。这正是后续各篇要逐层拆解的内容。五、本系列的工程底色:不是运气,是方法提前点明一件事,因为它贯穿后面所有篇章:这场攻关能走通,靠的不是运气好碰对了,而是一套刻意维持的工程纪律——全程台账驱动:每一处已验证生效的修改都即时记录(改了什么、为什么、怎么验证的),而不是事后凭记忆回溯;幂等补丁脚本:每一处源码修改都写成可重复运行、带已打补丁/未找到双重保护的脚本,而不是手改——这样在反复清理重建构建目录时,补丁能稳定复现;改完即验证:每打一个补丁,紧跟着用实际文件内容核实一遍,绝不仅凭脚本报告成功就相信(这条规矩是吃过亏才立下的,后面会讲到那次教训)。这套方法本身,会在系列最后一篇(机动篇)单独复盘。现在你只需要知道:后面看到的每一个解法,背后都有一条发现 → 定位 → 修改 → 验证的完整证据链,而不是试错试出来的玄学。六、小结与下一篇本篇把整个系列的为什么和做成了没有立住了:sglang 高性能后端在原生 Windows 上确实编译并运行成功,有可复验的三条铁证;官方没有 Windows 包是事实,但Windows 编译不出来 / 只能 WSL2是站不住的引申;真正的解法是从源码编出缺失的win_amd64子包,再用--no-deps让主包用上它;整场攻关按环境 → 源码方言 → MSVC 语义 → 架构裁剪与链接四层展开,是后续四篇的主线。从下一篇开始,我们正式进入实战。第 2 篇先处理 sgl-kernel 的前置依赖:手动编译 FlashInfer 的 Windows 兼容 fork——包括 CUDA 13.x 对齐补丁、多版本 VS 探测陷阱、--force-reinstall悄悄换掉 CUDA torch 等独立踩坑。第 3 篇再啃最容易被低估、却让无数人卡在第一步的环境关——为什么明明装了 VS2022,CMake 却报找不到 CUDA 工具集;为什么vcvarsall明明显示成功,cl.exe却凭空消失;以及那个反复让人栽跟头的生成器缓存。系列导航全 14 篇编译移植篇怎么把 sglang 从源码编出来00 · 系列总览01 · EPGF 环境地基与岔路口02 · 结论与可行性三铁证 --no-deps03 · 编译篇·前置FlashInfer Windows 源码编译04 · 编译篇·环境关VS 版本、venv 顺序、CUDA 多版本、生成器缓存05 · 移植篇(上)GCC 方言与 MSVC 预处理器严格性06 · 移植篇(下)常量求值、重载决议与编译器崩溃07 · 编译篇·收尾架构裁剪与 LNK2019 链接收尾08 · 方法论台账、幂等补丁脚本与多 AI 协作部署运行篇怎么跑起来并排障09 · 正确启动 SGLang Unlimited-OCR10 · 排障①推理输出乱码/数值错误根因定位11 · 排障②环境变量块超限导致 spawn 子进程崩溃12 · 性能调优RTX 3090 MoE triton autotune config13 · 长文档验证 代理/端口冲突坑 使用指南编译移植篇讲能不能编出来、怎么编部署运行篇讲编出来之后怎么跑通、怎么排障、怎么调优。两篇之间最关键的交叉点本机实际编译用的是 第 07 篇 产物sglang_kernel-0.4.3-cp310-abi3-win_amd64.whl而 第 09 篇 的启动命令正是加载它 Unlimited-OCR 模型。参考资料与延伸阅读以下为本文涉及的官方仓库、文档与规格站建议发布前点一遍确认可达Unlimited-OCR 官方仓库模型与项目源码SGLang 官方仓库SGLang 官方文档启动参数 / OpenAI 兼容 APIflashinfer-windowsWindows 兼容 fork编译前置vllm-windows同作者可对照的 Windows 移植思路PyTorch Windows CUDA 预编译索引cu130NVIDIA CUDA Toolkit 下载uv 官方文档Python 环境治理MSVC /Zc:preprocessor 标准预处理器MSVC 致命错误 C1001编译器内部错误nvcc -Xcompiler 转发 host 编译器选项CMake 生成器Visual Studio / NinjaRTX 3090 规格GA102 / sm_86共享内存 100KBCUDA 共享内存上限与 dynamic_shared_memory 限制Windows 子进程环境变量块限制CreateProcess / ~32KBOpenAI 兼容 API 参考推理调用

相关新闻