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