本页介绍了便携式内核库和优化内核库,它们是 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 架构进行微调。相反,优化内核库旨在提供可在各种平台上应用的性能良好的实现,而不是使用特定于单个平台的优化。
另一个重要的注意事项是,运算符覆盖率也不是优化内核库的目标。没有计划为每个核心 ATen 运算符添加优化内核;相反,会根据需要添加优化内核,以提高特定模型的性能。因此,与便携式内核库相比,优化内核库中的运算符覆盖率将非常有限。