本页介绍了随 ExecuTorch 一起发布的便携式内核库和优化内核库。如果您希望使用这些内核库执行 ExecuTorch 程序,或者想要实现自己的内核和内核库,建议阅读本页面内容。
ExecuTorch 内核库概览¶
ExecuTorch 程序编码描述了程序应执行的计算指令。这些指令中的许多将对应于调用特定的 ATen 操作符,例如 aten.convolution。然而,ExecuTorch 的核心设计原则之一是操作符的签名应与其实现分开。这意味着 ExecuTorch 运行时不会附带任何标准的 ATen 操作符实现;用户必须确保链接包含其 ExecuTorch 程序所需操作符实现的内核库,并配置 操作符注册以将操作符签名映射到所需的实现。这使得调整操作符的实现(如 aten.convolution)在执行 ExecuTorch 程序时变得容易;它允许用户选择完全符合其用例的独特性能、内存使用、电池使用等约束的操作符实现。
本质上,内核库只是一组遵循共同主题或设计原则的 ATen 算子实现集合。请注意,由于 ExecuTorch 的选择性构建过程(将在下一节讨论),算子实现是单独链接的。这意味着用户可以在构建中轻松混合不同的内核库,而不会增加构建体积。
ExecuTorch 默认包含两个内核库:可移植内核库和优化内核库,两者均提供 CPU 算子实现。
可移植内核库¶
可移植内核库在某种意义上是 ExecuTorch 所使用的“参考”内核库。开发可移植内核库时秉持以下目标:
正确性
提供 ATen 算子的直接实现,这些实现与 PyTorch ATen 库中原始算子的实现严格一致。
可读性 / 简洁性
提供清晰、可读的源代码,以便希望开发算子自定义实现的用户能够轻松理解该算子的预期行为。
可移植性
可移植内核应像 ExecuTorch 运行时一样具备可移植性;算子实现不应使用任何外部依赖,也不应使用任何未经批准的 C++ 特性。
操作符覆盖
作为 ExecuTorch 的“参考”内核库,Portable Kernel Library 旨在实现高度的算子覆盖。其目标是让 Portable Kernel Library 为所有列为 Core ATen 算子的算子提供实现。但请注意,Portable Kernel Library 的算子覆盖仍在持续完善中。
可移植内核库的主要目标是提供易于使用的算子实现,这些实现在大多数平台上能够“开箱即用”,并保证输出正确。性能并非可移植内核库的目标。事实上,为了优先考虑简洁性和可读性,许多瓶颈算子(如卷积和矩阵乘法)均以尽可能直接的方式实现。因此,如果仅使用可移植内核库,不应期望观察到快速的推理时间。然而,除了特定的瓶颈算子外,大多数算子足够简单,可移植内核库的直接实现仍应能提供足够的性能。二进制文件大小也不是可移植内核库的目标。
优化的内核库¶
优化内核库是随 ExecuTorch 一起发布的补充内核库,与可移植内核库不同,它旨在以牺牲可移植性和可读性为代价,提供面向性能的操作符实现。优化内核库中的许多操作符实现灵感来源于或基于 PyTorch ATen 库中的相应实现,因此在许多情况下,可以预期获得相同的性能水平。
一般来说,优化内核库中的算子通过以下两种方式之一进行优化:
使用 CPU 向量内部函数
使用优化的数学库,例如
sleef和OpenBLAS
尽管可移植性并非优化内核库的设计目标,但其实现并非专为特定 CPU 架构进行微调。相反,优化内核库旨在提供可在多种平台上应用的高性能实现,而非采用仅适用于单一平台的优化方案。
另一个重要说明是,算子覆盖范围也不是优化内核库的目标。我们并不计划为每一个 Core ATen 算子添加优化内核;相反,优化内核将根据实际需求按需添加,以提升特定模型的性能。因此,与可移植内核库相比,优化内核库的算子覆盖范围将更为有限。