每隔几个月,开源社区就会推出一个新的 AI 框架。其中大多数是在沉重的 C++ 内核之上封装 Python 绑定,或者堆叠了过高的抽象层,以至于仅运行时本身的开销就超过了它们所服务的模型。CatAI 则反其道而行之。它是一个完全用 C++ 编写的原生 AI 引擎,从张量数学底层开始构建。其目的并非在 PyTorch 之上再套一层友好的外壳,其核心在于从硬件边界开始,掌控每一字节的内存和每一个计算周期。
为什么还需要另一个引擎?
如果你曾将任何东西部署到生产环境,你一定深知其中的痛苦。将标准的深度学习技术栈拉入容器,你会眼睁睁看着镜像膨胀到数个 GB。依赖项之间相互冲突。Python 解释器增加了延迟。将算子路由到 CUDA 或 CPU 的调度器会引入细微的开销,而一旦这些开销消失在十几层嵌套框架中,就变得无法进行性能分析。对于边缘设备、嵌入式机器人或延迟敏感型后端来说,这种代价是实实在在的。一个纯 C++ 引擎消除了中间环节。它直接与操作系统和硅片通信,没有垃圾回收,没有全局解释器锁(GIL),也没有语言之间的序列化繁琐过程。
CatAI 将此视为一项特性,而非一种妥协。该项目正在用 C++ 从零开始编写,因为作者想要精确决定张量如何在 RAM 中存储、如何在缓存层级中移动,以及如何在线程间调度内核。这并非自虐。当你试图从有限的硬件中压榨性能时,这是保证行为可预测的唯一途径。
“从零开始”究竟意味着什么
在大多数现代框架中,张量数学是通过对 cuDNN、oneMKL 或 MPS 等厂商库的不透明调用来处理的。为了快速交付,这完全合乎逻辑,但它隐藏了操作的底层机制。CatAI 正在编写自己的核心张量数学和内存布局。这意味着要设计承载多维数组的基础数据结构,选择步长(strides)和偏移量(offsets)的计算方式,并根据访问模式决定是将数据存储为行优先(row-major)、列优先(column-major)还是自定义的分块(tiled)格式。
这是深层的系统级工作。当你亲手编写矩阵乘法内核时,你不再思考 torch.matmul,而是开始思考 L1 缓存行、寄存器压力和循环分块(loop tiling)。你会根据目标 CPU 的 SIMD 宽度,决定是使用 32x32 还是 64x64 的分块。你将内存分配对齐到 64 字节边界,以确保 AVX-512 加载不会跨越缓存行。你会质疑 std::vector 是否是张量存储的合适容器,或者自定义的 arena 分配器是否能在整个推理图中提供更好的局部性并实现零碎片化。
内存布局同样至关重要。如果以 channels-first 模式访问 channels-last 的图像数据,一个朴素的 n 维数组会摧毁性能。在 CatAI 中,这些布局是“一等公民”,而不是在导出时由图优化器处理的后续补救措施。
优化思维
在开始计算纳秒之前,“裸机优化”听起来就像个噱头。它意味着融合算子(fusing operations),使中间结果永远不会离开 CPU 寄存器或 L1 缓存。它意味着将 layer-norm 及其后的 GELU 实现为一个单一内核,从而节省一次完整的 DRAM 往返访问。它意味着编写自己的线程池,而不是依赖 OpenMP 的默认设置,因为你知道你的工作负载是突发性的,你不希望运行时在每次前向传播时都进行线程的创建和合并。
这也意味着要明白什么时候不该写汇编。有时编译器对循环的向量化处理比手写的内联函数(intrinsics)效果更好。这种纪律在于测量:进行性能分析、提出假设、改变一个变量,然后再次进行性能分析。这个引擎是由热爱这种枯燥磨练的人们构建的。如果你曾花掉一个下午重写卷积循环,只为在批处理中节省两毫秒,那么你已经理解了这种文化。
我们需要谁
这不是一个人的独角戏。从零开始构建后端需要截然不同的技能,而这些技能很难在同一个人身上重叠。如果你正在阅读这段文字并考虑是否加入,以下是你的潜在切入点:
C++ 开发者:熟悉现代标准,但也知道何时模板会导致编译膨胀(compilation bloat)。在必要时能熟练使用原始指针(raw pointers),在合适时使用智能指针(smart pointers),并且像关注语法糖一样关注二进制文件大小。
数学专家:能为非标准激活函数推导反向传播梯度,能分析混合精度训练中的数值稳定性,并在算法转化为代码之前进行优化。如果你能解释为什么 log-sum-exp 技巧很重要,那么你的思维方式就对了。
底层内存专家:思考分配器(allocators)、缺页中断(page faults)和 NUMA 拓扑结构。引擎需要用于图执行的内存池、用于内核(kernels)的暂存缓冲区(scratch buffers),以及在训练步骤间重用张量存储且不产生泄漏或碎片化的策略。
系统工程师:理解错误的系统调用(syscall)如何导致整个训练循环停滞。调度、I/O 和同步原语(synchronization primitives)是连接数学逻辑的纽带。
你不需要在所有四个领域都是世界级的专家。大多数贡献者会从负责一个内核或一个分配器开始,随着架构的巩固逐步学习其他部分。
架构与自定义数学
后端逻辑正在协作构建,这始于架构辩论。引擎将使用静态计算图(在运行时前定义并优化整个模型),还是支持带有自动微分 Tape 的 eager execution?自动微分将如何表示——运算符重载、源码转换还是图 IR?这些决定塑造了其他一切。
自定义神经网络数学不仅仅意味着重新实现标准层,还意味着拥有发明新层的自由。如果你想要一个带有非标准稀疏内核的卷积变体,或者一个文献中没有名称的激活函数,你可以编写 C++ 的前向和后向传播,并将其直接插入引擎。无需与 Python API 作斗争,也无需进行猴子补丁(monkey-patching)。数学即代码,代码即接口。
如何参与
如果这引起了你的共鸣,完整的项目分解和当前路线图已在作者的 Dev.to 文章中详细记录。你可以阅读具体细节,查看目前已构建的内容,并准确了解需要哪些帮助。
此外,还有一个 Telegram 群组,欢迎任何想要交流、提问或在不立即提交 pull request 的情况下关注进度的人加入。
真正的启示
现代 AI 技术栈已变成一个黑盒。我们将框架视为“魔法设备”:输入数据,输出模型,并寄希望于这种不透明性在部署时不会让我们吃亏。CatAI 拒绝这种安逸。这种构建方式速度较慢。你会编写更多代码,调试更多的段错误(segfaults),并重新审视高层框架为你隐藏的假设。但你也会理解机器为何如此运作。在这样一个每个人都在竞相抽象硬件的行业中,反其道而行之、触碰底层(touching the metal)具有真正的价值。这种理解正是“调用 API 的人”与“构建系统的人”之间的区别。
