在 Apple 平台上集成和运行 ExecuTorch¶
适用于 iOS 和 macOS 的 ExecuTorch 运行时作为预构建的 .xcframework 二进制目标的集合进行分发。这些目标与 iOS 和 macOS 设备及模拟器兼容,并且可用于发布和调试模式:
executorch
- 主要 Runtime 组件backend_coreml
- Core ML 后端backend_mps
- MPS 后端backend_xnnpack
- XNNPACK 后端kernels_custom
- LLM 的自定义内核kernels_optimized
- 优化的内核kernels_portable
- 可移植内核(用作参考的天真实现)kernels_quantized
- 量化内核
将您的二进制文件与 ExecuTorch 运行时以及导出的 ML 模型使用的任何后端或内核链接。建议将核心运行时链接到直接使用 ExecuTorch 的组件,并将内核和后端链接到主应用程序目标。
注意:要访问日志,请链接到 ExecuTorch 运行时的 Debug 版本,即框架。为了获得最佳性能,请始终链接到交付项的 Release 版本(不带后缀的交付项),该版本已删除所有日志记录开销。executorch_debug
_debug
集成¶
Swift 包管理器¶
预构建的 ExecuTorch 运行时、后端和内核以 Swift PM 包的形式提供。
Xcode 代码¶
在 Xcode 中,转到 。将 ExecuTorch 存储库的 URL 粘贴到搜索栏中并选择它。确保在特定日期将分支名称更改为所需的 ExecuTorch 版本,格式为 “swiftpmFile > Add Package Dependencies
然后选择哪个 ExecuTorch 框架应该链接到哪个目标。
单击下面的屏幕截图,观看有关如何在 iOS 上添加包并运行简单 ExecuTorch 模型的演示视频。
命令行界面¶
将 ExecuTorch 上的包和目标依赖项添加到您的包文件中,如下所示:
// swift-tools-version:5.9
import PackageDescription
let package = Package(
name: "YourPackageName",
platforms: [
.iOS(.v17),
.macOS(.v10_15),
],
products: [
.library(name: "YourPackageName", targets: ["YourTargetName"]),
],
dependencies: [
// Use "swiftpm-<version>.<year_month_day>" branch name for a nightly build.
.package(url: "https://github.com/pytorch/executorch.git", branch: "swiftpm-0.4.0")
],
targets: [
.target(
name: "YourTargetName",
dependencies: [
.product(name: "executorch", package: "executorch"),
.product(name: "backend_xnnpack", package: "executorch"),
.product(name: "kernels_portable", package: "executorch"),
// Add other backends and kernels as needed.
]),
]
)
然后检查一切是否正常工作:
cd path/to/your/package
swift package resolve
# or just build it
swift build
本地构建¶
集成 ExecuTorch 运行时的另一种方法是从本地源代码构建必要的组件并链接到它们。这条路线更复杂,但肯定是可行的。
安装 Xcode 15+ 和命令行工具:
xcode-select --install
克隆 ExecuTorch:
git clone https://github.com/pytorch/executorch.git --depth 1 --recurse-submodules --shallow-submodules && cd executorch
设置 Python 3.10+ 并激活虚拟环境:
python3 -m venv .venv && source .venv/bin/activate && pip install --upgrade pip
./install_requirements.sh --pybind coreml mps xnnpack
# Optional dependencies for Core ML backend.
./backends/apple/coreml/scripts/install_requirements.sh
# And MPS backend.
./backends/apple/mps/install_requirements.sh
安装 CMake:
从 CMake 网站下载 macOS 二进制分发版,打开文件,移动到目录,然后运行以下命令以安装 CMake 命令行工具:.dmg
CMake.app
/Applications
sudo /Applications/CMake.app/Contents/bin/cmake-gui --install
使用提供的脚本构建 .xcframeworks:
./build/build_apple_frameworks.sh --help
例如,以下调用将为 Apple 平台构建 ExecuTorch 运行时和所有当前可用的内核和后端:
./build/build_apple_frameworks.sh --coreml --mps --xnnpack --custom --optimized --portable --quantized
如果需要,将标志附加到上述命令以使用调试符号构建二进制文件。--Debug
构建成功完成后,可以在目录中找到生成的框架。
将它们复制到您的项目中,并将它们与您的目标相关联。cmake-out
联动¶
ExecuTorch 通过在静态字典中注册 app 启动来初始化其后端和内核(算子)。如果您在运行时遇到 “unregistered kernel” 或 “unregistered backend” 等错误,您可能需要显式强制加载某些组件。在 Xcode 构建配置中使用 or 链接器标志,以确保尽早注册组件。-all_load
-force_load
下面是一个 Xcode 配置文件 () 的示例:.xcconfig
ET_PLATFORM[sdk=iphonesimulator*] = simulator
ET_PLATFORM[sdk=iphoneos*] = ios
ET_PLATFORM[sdk=macos*] = macos
OTHER_LDFLAGS = $(inherited) \
-force_load $(BUILT_PRODUCTS_DIR)/libexecutorch-$(ET_PLATFORM)-release.a \
-force_load $(BUILT_PRODUCTS_DIR)/libbackend_coreml-$(ET_PLATFORM)-release.a \
-force_load $(BUILT_PRODUCTS_DIR)/libbackend_mps-$(ET_PLATFORM)-release.a \
-force_load $(BUILT_PRODUCTS_DIR)/libbackend_xnnpack-$(ET_PLATFORM)-release.a \
-force_load $(BUILT_PRODUCTS_DIR)/libkernels_optimized-$(ET_PLATFORM)-release.a \
-force_load $(BUILT_PRODUCTS_DIR)/libkernels_quantized-$(ET_PLATFORM)-release.a
对于 Debug 构建配置,请在库文件名中替换为 。请记住在调试模式下链接到 ExecuTorch 运行时 (),即使其他组件是为 Release 构建的,以在需要时保留日志。release
debug
libexecutorch
您可以在 Xcode 中将此类配置文件分配给您的目标:
将文件添加到您的项目中。
.xcconfig
导航到项目的 Info 选项卡。
在 Release (or Debug) 模式的构建配置中选择配置文件。
运行时 API¶
查看 C++ 运行时 API 和 Tensors 教程,了解有关如何加载和运行导出的模型的更多信息。建议使用适用于 macOS 或 iOS 的 C++ API,如果需要,请使用 Objective-C++ 和 Swift 代码进行包装,以便为其他组件公开。请参阅 演示应用程序 作为此类设置的示例。
一旦与运行时框架链接,目标现在可以导入所有 ExecuTorch 公共标头。例如,在 Objective-C++ 中:executorch
#import <ExecuTorch/ExecuTorch.h>
#import <executorch/extension/module/module.h>
#import <executorch/extension/tensor/tensor.h>
或者在 Swift 中:
import ExecuTorch
注意:导入 ExecuTorch 伞形头文件(或 Swift 中的 ExecuTorch 模块)仅提供对日志记录 API 的访问。您仍然需要根据需要显式导入其他运行时标头,例如 .除了下面描述的日志记录之外,Objective-C 或 Swift 中不支持其他运行时 API。module.h
注意:日志在 ExecuTorch 框架的发布版本中被剥离。要保留日志记录,请在开发过程中使用 debug build。
伐木¶
我们提供了额外的 API 来登录 Objective-C 和 Swift,作为内部 ExecuTorch 机制的轻量级包装器。要使用它,只需在 Objective-C 中导入主框架头文件即可。然后使用接口(或 Swift 中的类)订阅您自己的协议实现(或在 Swift 中)来监听日志事件。ExecuTorchLog
Log
ExecuTorchLogSink
LogSink
#import <ExecuTorch/ExecuTorch.h>
#import <os/log.h>
@interface MyClass : NSObject<ExecuTorchLogSink>
@end
@implementation MyClass
- (instancetype)init {
self = [super init];
if (self) {
#if DEBUG
[ExecuTorchLog.sharedLog addSink:self];
#endif
}
return self;
}
- (void)dealloc {
#if DEBUG
[ExecuTorchLog.sharedLog removeSink:self];
#endif
}
#if DEBUG
- (void)logWithLevel:(ExecuTorchLogLevel)level
timestamp:(NSTimeInterval)timestamp
filename:(NSString *)filename
line:(NSUInteger)line
message:(NSString *)message {
NSString *logMessage = [NSString stringWithFormat:@"%@:%lu %@", filename, (unsigned long)line, message];
switch (level) {
case ExecuTorchLogLevelDebug:
os_log_with_type(OS_LOG_DEFAULT, OS_LOG_TYPE_DEBUG, "%{public}@", logMessage);
break;
case ExecuTorchLogLevelInfo:
os_log_with_type(OS_LOG_DEFAULT, OS_LOG_TYPE_INFO, "%{public}@", logMessage);
break;
case ExecuTorchLogLevelError:
os_log_with_type(OS_LOG_DEFAULT, OS_LOG_TYPE_ERROR, "%{public}@", logMessage);
break;
case ExecuTorchLogLevelFatal:
os_log_with_type(OS_LOG_DEFAULT, OS_LOG_TYPE_FAULT, "%{public}@", logMessage);
break;
default:
os_log(OS_LOG_DEFAULT, "%{public}@", logMessage);
break;
}
}
#endif
@end
Swift 版本:
import ExecuTorch
import os.log
public class MyClass {
public init() {
#if DEBUG
Log.shared.add(sink: self)
#endif
}
deinit {
#if DEBUG
Log.shared.remove(sink: self)
#endif
}
}
#if DEBUG
extension MyClass: LogSink {
public func log(level: LogLevel, timestamp: TimeInterval, filename: String, line: UInt, message: String) {
let logMessage = "\(filename):\(line) \(message)"
switch level {
case .debug:
os_log(.debug, "%{public}@", logMessage)
case .info:
os_log(.info, "%{public}@", logMessage)
case .error:
os_log(.error, "%{public}@", logMessage)
case .fatal:
os_log(.fault, "%{public}@", logMessage)
default:
os_log("%{public}@", logMessage)
}
}
}
#endif
注意:在该示例中,当代码不是为 Debug 模式构建时,即宏未定义或等于零,则会有意剥离日志。DEBUG
调试¶
如果要链接到 ExecuTorch 框架的 Debug 版本,请在调试会话中使用以下 LLDB 命令配置调试器以正确映射源代码:
settings append target.source-map /executorch <path_to_executorch_source_code>
故障 排除¶
执行速度慢¶
确保导出的模型使用适当的后端,例如 XNNPACK、Core ML 或 MPS。如果调用了正确的后端,但性能问题仍然存在,请确认您正在针对后端运行时的 Release 版本进行链接。
为了获得最佳性能,也请在发布模式下链接 ExecuTorch 运行时。如果需要调试,您可以将 ExecuTorch 运行时保持在 Debug 模式,对性能的影响最小,但保留日志记录和调试符号。
斯威夫特 PM¶
如果您在使用 Swift PM 时遇到校验和不匹配错误,请使用 Xcode 菜单 () 或以下命令清除包缓存:File > Packages > Reset Package Caches
rm -rf <YouProjectName>.xcodeproj/project.xcworkspace/xcshareddata/swiftpm \
~/Library/org.swift.swiftpm \
~/Library/Caches/org.swift.swiftpm \
~/Library/Caches/com.apple.dt.Xcode \
~/Library/Developer/Xcode/DerivedData
注意:在执行 terminal 命令之前,请确保 Xcode 已完全退出,以避免与活动进程冲突。