DeepSeek开源DeepJIT:统一华为昇腾和英伟达CUDA接口


DeepJIT 是 DeepSeek 开源的一个轻量级、header-only 的 C++20 运行时库,用于在 NVIDIA CUDA GPU 和华为昇腾 NPU 上运行时编译(JIT)内核代码。它旨在为 C++/Python 扩展开发者提供统一的接口,以处理内核源代码的编译、二进制缓存、加载到设备以及启动等流程。

核心特性

DeepJIT 的设计围绕性能与易用性展开,主要特性包括:

  • 跨后端统一接口:为 CUDA 和昇腾后端提供一致的 compile/load/launch 工作流,开发者可以使用相同的 API 操作不同硬件。
  • 多级内核缓存:支持内存和磁盘两级缓存,缓存键综合了源代码、头文件依赖、编译器版本、编译选项及用户自定义签名等信息,避免重复编译。
  • 分布式共享缓存:支持在分布式文件系统上共享缓存目录,让多个用户、进程或计算节点复用已编译的内核,显著减少编译延迟。
  • 延迟初始化:将设备和编译器的发现推迟到运行时首次使用时,加速启动过程。
  • PyTorch 集成:默认使用当前 PyTorch 的 CUDA 或 torch_npu 流,并通过 pybind11 暴露 get_jit() 接口,方便在 Python 扩展中调用。
  • 编译控制与诊断:支持配置编译选项、检查编译元数据,以及导出 CUDA PTX/SASS 或昇腾汇编代码用于调试。

写一次CUDA代码就能在英伟达和华为芯片上跑,这个C++库凭什么敢这么吹!

同一段CUDA内核代码,改一行都不用,直接扔到华为昇腾NPU上跑,你信吗!

DeepSeek在2026年9月8日开源了一个叫DeepJIT的C++库,官方描述是“为英伟达CUDA GPU和华为昇腾NPU提供共享的内核编译接口”。简单说,这个库让你写一份设备代码,能编译到两种完全不同的芯片上运行。听上去是不是有点反直觉,英伟达的CUDA和华为的昇腾NPU,连指令集都不一样,怎么可能用同一个运行时管起来!

但等一下,这个“统一”到底统一了什么,又没统一什么,才是真正值得聊的事。


统一接口听着很美,但统一不了编译器

DeepJIT的核心卖点,是让开发者用deep_jit::Runtimedeep_jit::Runtime这两个模板实例,跑同一套compile/load/launch流程。听起来像是写一份代码就能跨平台。

但是你仔细看它的后端要求就明白了,CUDA后端依赖NVCC把CUDA源码编译成CUBIN,昇腾后端依赖毕昇编译器和ld.lld把昇腾内核源码编译链接。两套编译器,两条完全不同的代码路径。

DeepJIT统一的是编译之前和编译之后的环节,也就是配置管理、缓存键计算、内核加载和启动调用这些“外围工作”。编译本身,它不管,也管不了。你的CUDA代码用global修饰,昇腾内核用global aicore修饰,写法就不一样。这就好比一个快递公司的统一前台,帮你接收和派送包裹,但包裹里面装的是什么、怎么打包,还得你自己按不同快递公司的规矩来。

那为什么还有人需要这个东西?因为它解决了一个更实际的问题:当你同时维护CUDA和昇腾两套内核代码时,你不需要写两套缓存管理逻辑、两套编译配置系统、两套Python绑定接口。DeepJIT把两边都长得差不多但实现完全不同的“杂活”,收进了一个统一的壳子里。


真正的杀手锏藏在缓存键里

大多数人看DeepJIT,第一眼注意到的是跨后端接口。但真正让这个库有意思的地方,是它的缓存键设计。

DeepJIT的缓存键综合了四类信息:内核源码、被追踪的头文件依赖、编译器版本和有效编译选项、以及应用层提供的自定义依赖签名。前三个还好理解,关键是第四个,应用提供的依赖签名。

这意味着什么?假设你写了一个矩阵乘法内核,它的性能取决于GPU的SM数量、共享内存大小、甚至你在启动时指定的block维度。这些参数不会体现在源码里,但会直接影响编译产物的正确性和性能。DeepJIT允许你把这些“运行时才知道的信息”塞进缓存键,让不同配置的内核各自独立缓存,互不污染。

但是,等一下。这个设计也有代价。你的缓存键越复杂,计算缓存键本身的开销就越大。如果你每次调用内核都要先算一遍SHA-256哈希,再加上头文件依赖树的遍历,这个开销在极短的内核调用场景下可能反而成为瓶颈。

所以DeepJIT做了一个很聪明的取舍:它把设备和编译器的发现推迟到运行时首次使用时才执行,也就是所谓的“延迟初始化”。你创建一个JIT运行时对象的时候,它什么都不做,等到你第一次调用compile(),它才去查你的CUDA版本、找NVCC路径、探测GPU架构。这省掉的是启动阶段的固定开销,但并没有消除缓存键计算本身的成本。

这就怪了,既然缓存键算得这么细,那缓存命中率到底怎么样?


分布式共享缓存,才是DeepSeek真正想做的事

DeepJIT有一个功能,在竞品里很少见:它允许你把同一个缓存目录挂载到分布式文件系统上,让多个用户、多个进程、甚至多个计算节点共享同一份已编译内核缓存。

这个设计直接回应了大规模AI训练的一个真实痛点。想象一下,你在一个128卡的集群上跑训练任务,每张卡都要编译同一组内核。如果每张卡各自编译一遍,那就是128倍的重复编译时间。如果有一个共享缓存目录,第一张卡编译完,剩下127张直接读缓存。

这就是DeepSeek自己训练DeepSeek-V3这类大模型时踩过的坑。他们的训练框架里用了大量JIT编译的通信内核和计算内核,如果没有跨节点的缓存共享,每次启动训练任务都要花大量时间在编译上。

但是,分布式共享缓存有一个隐藏的前提条件:文件系统必须提供POSIX语义,也就是说,多个进程同时读写同一个文件时,行为必须是可预测的。很多高性能分布式文件系统,比如某些为AI训练优化的并行文件系统,为了性能牺牲了一部分POSIX一致性保证。这就意味着,DeepJIT的共享缓存在某些集群上可能根本跑不起来。

事情没那么简单!

而且你要注意,共享缓存带来的是“读多写少”的场景。如果大量进程同时遇到缓存未命中,同时尝试编译同一个内核、同时写同一个缓存文件,就需要文件锁或者原子操作来保证不会写坏。DeepJIT的README里没有详细说明它怎么处理并发写入冲突,这本身就是一个值得警惕的信号。


跟Triton比,DeepJIT选了另一条路

说到GPU内核JIT编译,绕不开Triton。Triton是OpenAI开源的一套Python DSL,你用Python写内核,它帮你编译到GPU上跑。

Triton的缓存机制也很成熟:它用SHA-256哈希函数源码和依赖,把编译产物存到~/.triton/cache目录下,还支持通过RemoteCacheManager接入Redis做分布式缓存。从功能上看,Triton该有的缓存层都有。

但Triton和DeepJIT走的是完全不同的路线。

Triton让你用一种新的语言写内核。你学的是Triton的语法、Triton的编程模型、Triton的优化方式。你写出来的代码不能在CUDA上直接编译,更不能在昇腾上直接编译。Triton自己负责把中间表示翻译到不同后端。

DeepJIT不发明新语言。你写的是原生CUDA C++,原生昇腾内核代码。DeepJIT只负责编译和缓存这两件事。它不碰你的代码怎么写,只关心怎么把你的代码更快地变成一个可以加载到设备上的二进制。

这两条路线没有绝对的好坏。Triton的上手门槛更低,但灵活性也受限于Triton的表达能力。DeepJIT的门槛更高,你必须懂CUDA或者昇腾编程,但你能做的事情没有上限。

那DeepJIT有没有比Triton做得更好的地方?有一个:它的分布式缓存不依赖Redis这样的外部服务。Triton的RedisRemoteCacheBackend需要你部署和维护一个Redis实例,DeepJIT直接用文件系统做共享,少了一个运维负担。

反过来说,Triton的缓存后端可以插拔替换,DeepJIT目前只有文件系统一种选择。如果你的集群文件系统不支持POSIX语义,Triton还能换成Redis,DeepJIT就没办法了。


CuPy的NVRTC路线,快但窄

CuPy是另一个值得对比的对象。它是一个用Python做GPU计算的库,兼容NumPy的API,底层用NVRTC做运行时编译。

NVRTC是英伟达提供的运行时编译库,它把CUDA C++源码直接编译成PTX中间表示,不需要调用NVCC这个外部进程。CuPy的主编译路径就走NVRTC,避免了对NVCC的依赖。

从速度上看,NVRTC路线确实有优势。调用一个库函数比启动一个外部进程要快得多。如果你的场景是大量小内核的频繁编译和启动,NVRTC的延迟优势会很明显。

但是NVRTC有一个硬限制:它只支持CUDA,而且只支持NVIDIA GPU。CuPy虽然也支持AMD的ROCm,那是通过HIPRTC实现的,跟NVRTC是两套东西。至于华为昇腾,CuPy完全不支持。

所以CuPy和DeepJIT根本不在一个赛道上竞争。CuPy的目标用户是Python数据科学家,他们想要的是NumPy风格的GPU加速,不需要关心底层编译细节。DeepJIT的目标用户是C++/Python扩展开发者,他们要写高性能内核库,需要一个可靠的编译和缓存基础设施。

但是,NVRTC路线的速度优势对DeepJIT是一个提醒。DeepJIT的CUDA后端目前走的是NVCC外部进程路线,每次编译都要fork一个子进程、等待它完成、读回产物。这个开销在编译频率高的场景下会累积。如果DeepJIT未来能加一条NVRTC的可选路径,对那些不需要复杂链接的内核来说会是一个显著的提速。

前提是,它得先解决NVRTC不支持CUBIN链接、不支持完整C++标准库这些限制。


跨后端的代价,比你想的要大

回到最开始的问题:写一份代码跑两种芯片,听起来很美好,但实际用起来是什么体验?

你写一个CUDA内核,用dim3指定grid和block维度。昇腾内核没有dim3这个概念,它用num_blocks这样的参数来表示并行度。DeepJIT的launch接口在CUDA后端接受grid_dim和block_dim,在昇腾后端接受num_blocks。这些差异不是DeepJIT能抹平的,它们来自硬件架构本身的区别。

这就引出一个更深层的问题:跨后端统一接口的价值,到底有多大?

如果你的代码库只需要支持CUDA,用DeepJIT的CUDA后端没什么坏处,你获得了一套成熟的缓存系统和Python绑定。如果你的代码库需要同时支持CUDA和昇腾,DeepJIT帮你省掉的是“维护两套缓存逻辑”的工作量,大概能省掉30%到40%的样板代码,剩下的60%到70%——内核本身的编写和调试——还是得各写各的。

但是,如果你的代码库只需要支持昇腾,DeepJIT的CUDA后端就是死代码。它会增加你的编译时间、增加你的依赖复杂度,但你永远用不到。

所以DeepJIT真正的目标用户画像很清楚:你是一个AI基础设施团队,你在同时维护英伟达和昇腾两个平台的训练或推理框架,你的团队有足够的C++工程能力来理解和调试这套系统。如果你只是写一个跑在A100上的单平台训练脚本,DeepJIT可能不是为你准备的。

如果DeepJIT对你没用,那就别用。


发布一天就上热门,但有两个功能还没做完

DeepJIT在GitHub上发布第一天就获得了243个star和67个fork,对于一个纯C++工具库来说,这个热度相当可观。

但是,它的README里明明白白写着两个功能还在开发中:一个是“缓存预热”,根据历史缓存记录预测未来可能需要的内核,提前编译好,减少运行时的编译延迟;另一个是“Python编译API”,允许你直接从Python传内核源码给DeepJIT编译。

第一个功能如果做成了,对大规模训练场景的价值会很大。它意味着你的训练任务在启动之前,就可以根据上次运行的记录,把这次可能用到的内核全部预编译好。训练一开始,所有内核都已经在缓存里了,零编译延迟。

第二个功能就更直接了。目前你要用DeepJIT,必须写C++扩展,通过pybind11把接口暴露给Python。如果有了纯Python的编译API,你可以直接在Jupyter Notebook里写CUDA内核、编译、运行,不用碰CMake,不用配C++工具链。这会把DeepJIT的受众从“会写C++的基础设施工程师”扩大到“会用Python的算法研究员”。

但这两个功能目前都不可用。你现在拿到的DeepJIT,是一个功能完整的CUDA/昇腾内核JIT运行时,但不是一个零门槛的Python内核编译工具。

还有一个数字值得注意:DeepJIT的CUDA后端要求NVCC 12.9+,但CUDA头文件只要12.4+。这个版本组合意味着什么?意味着你的PyTorch如果编译时用的CUDA版本低于12.9,你可能需要单独安装一个更新的NVCC。而PyTorch官方发布的wheel目前覆盖CUDA 12.6、12.8、12.9三个版本。如果你用的是12.6的PyTorch,你要么升级到12.9的wheel,要么在系统里额外装一套12.9的NVCC。这个依赖链条,在真实部署中会制造不少麻烦。

你准备好为了一个缓存库,升级你整个CUDA工具链了吗?

DeepSeek在发布DeepSeek-V4.1 Flash模型的同一天开源了DeepJIT,这个时间点的选择不是巧合。他们的训练基础设施团队在内部已经用这套东西跑了很久,现在把它开源出来,既是对社区的回馈,也是在为未来的生态布局。当越来越多的框架选择DeepJIT作为内核编译层,DeepSeek在AI基础设施领域的话语权就会多一个筹码。

但生态这个东西,是靠用出来的,不是靠开源出来的。