在 iOS 上使用 ExecuTorch¶
ExecuTorch 通过 Objective-C、Swift 和 C++ 支持 iOS 和 macOS。ExecuTorch 还提供后端,可利用 Core ML 和 Metal Performance Shaders (MPS) 在 Apple 平台上进行硬件加速执行。
集成¶
适用于 iOS 和 macOS 的 ExecuTorch 运行时以预构建的 .xcframework 二进制目标集合形式分发。这些目标与 iOS 和 macOS 设备以及模拟器兼容,并提供发布 (release) 和调试 (debug) 模式。
executorch
- 主运行时组件backend_coreml
- Core ML 后端backend_mps
- MPS 后端backend_xnnpack
- XNNPACK 后端kernels_custom
- 适用于 LLMs 的自定义内核kernels_optimized
- 优化内核kernels_portable
- 可移植内核(用作参考的朴素实现)kernels_quantized
- 量化内核
将您的二进制文件与 ExecuTorch 运行时以及导出的 ML 模型使用的任何后端或内核链接。建议将核心运行时链接到直接使用 ExecuTorch 的组件,并将内核和后端链接到主应用目标。
注意:要访问日志,请链接 ExecuTorch 运行时的 Debug 构建版本,即 executorch_debug
框架。为了获得最佳性能,请始终链接交付件的 Release 版本(不带 _debug
后缀的版本),这些版本已移除所有日志开销。
Swift Package Manager¶
预构建的 ExecuTorch 运行时、后端和内核可通过 Swift PM 包获取。
Xcode¶
在 Xcode 中,前往 文件 > 添加包依赖项
。将 ExecuTorch 仓库的 URL 粘贴到搜索栏中并选择它。确保将分支名称更改为所需 ExecuTorch 版本,格式为“swiftpm-
然后选择哪个 ExecuTorch 框架应链接到哪个目标。
点击下方截图观看演示视频,了解如何在 iOS 上添加软件包并运行简单的 ExecuTorch 模型。

命令行界面 (CLI)¶
像这样将 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.6.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+ 和 Command Line Tools
xcode-select --install
克隆 ExecuTorch
git clone -b release/0.6 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
# CoreML-only requirements:
./backends/apple/coreml/scripts/install_requirements.sh
# MPS-only requirements:
./backends/apple/mps/install_requirements.sh
安装 CMake
从 CMake 网站下载 macOS 二进制分发版本,打开 .dmg
文件,将 CMake.app
移动到 /Applications
目录,然后运行以下命令安装 CMake 命令行工具
sudo /Applications/CMake.app/Contents/bin/cmake-gui --install
使用提供的脚本构建 .xcframeworks
./scripts/build_apple_frameworks.sh --help
例如,以下命令将为 Apple 平台构建 ExecuTorch 运行时以及所有可用的内核和后端,包括 Release 和 Debug 模式
./scripts/build_apple_frameworks.sh --Release --Debug --coreml --mps --xnnpack --custom --optimized --portable --quantized
构建成功完成后,生成的框架可在 cmake-out
目录中找到。将它们复制到您的项目中并链接到您的目标。
链接¶
ExecuTorch 在应用启动期间通过在静态字典中注册后端和内核(运算符)来初始化它们。如果在运行时遇到“未注册内核”或“未注册后端”等错误,您可能需要显式地强制加载某些组件。在您的 Xcode 构建配置中使用 -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_debug_$(ET_PLATFORM).a \
-force_load $(BUILT_PRODUCTS_DIR)/libbackend_coreml_$(ET_PLATFORM).a \
-force_load $(BUILT_PRODUCTS_DIR)/libbackend_mps_$(ET_PLATFORM).a \
-force_load $(BUILT_PRODUCTS_DIR)/libbackend_xnnpack_$(ET_PLATFORM).a \
-force_load $(BUILT_PRODUCTS_DIR)/libkernels_optimized_$(ET_PLATFORM).a \
-force_load $(BUILT_PRODUCTS_DIR)/libkernels_quantized_$(ET_PLATFORM).a
注意:在上面的示例中,我们链接到 ExecuTorch 运行时的 Debug 版本 (libexecutorch_debug
) 以保留日志。通常,这不会对性能产生太大影响。然而,请记住链接到运行时的 release 版本 (libexecutorch
) 以获得最佳性能且不包含日志。
您可以在 Xcode 中将此类配置文件分配给您的目标
将
.xcconfig
文件添加到您的项目中。导航到项目的 Info 选项卡。
在 Release(或 Debug)模式的构建配置中选择配置文件。
运行时 API¶
ExecuTorch 提供原生的 Objective-C API,可自动桥接到 Swift,用于与运行时交互。这些 API 是 extension/tensor 和 extension/module 中核心 C++ 组件的封装,为 Apple 平台开发者提供更符合习惯的体验。
注意:这些 Objective-C/Swift API 目前处于实验阶段,可能会发生变化。
导入¶
一旦链接到 executorch
框架,您就可以导入必要的组件。
Objective-C (Objective-C++)
// Import the main umbrella header for Module/Tensor/Value wrappers.
#import <ExecuTorch/ExecuTorch.h>
// If using C++ directly alongside Objective-C++, you might still need C++ headers.
#import <executorch/extension/module/module.h>
#import <executorch/extension/tensor/tensor.h>
Swift
import ExecuTorch
示例¶
这里提供一个简洁的示例,演示如何使用 Objective-C 和 Swift API 加载模型、准备输入、运行推理和处理输出。假设您有一个 MobileNet v3 模型 (mv3.pte
),它接受一个 [1, 3, 224, 224]
浮点 tensor 作为输入并输出 logits。
Objective-C
NSString *modelPath = [[NSBundle mainBundle] pathForResource:@"mv3" ofType:@"pte"];
// Create a module with the model file path. Nothing gets loaded into memory just yet.
ExecuTorchModule *module = [[ExecuTorchModule alloc] initWithFilePath:modelPath];
NSError *error; // Optional error output argument to learn about failures.
// Force-load the program and 'forward' method. Otherwise, it's loaded at the first execution.
[module loadMethod:@"forward" error:&error];
float *imageBuffer = ...; // Existing image buffer.
// Create an input tensor referencing the buffer and assuming the given shape and data type.
ExecuTorchTensor *inputTensor = [[ExecuTorchTensor alloc] initWithBytesNoCopy:imageBuffer
shape:@[@1, @3, @224, @224]
dataType:ExecuTorchDataTypeFloat];
// Execute the 'forward' method with the given input tensor and get output values back.
NSArray<ExecuTorchValue *> *outputs = [module forwardWithTensor:inputTensor error:&error];
// Get the first output value assuming it's a tensor.
ExecuTorchTensor *outputTensor = outputs.firstObject.tensor;
// Access the output tensor data.
[outputTensor bytesWithHandler:^(const void *pointer, NSInteger count, ExecuTorchDataType dataType) {
float *logits = (float *)pointer;
// Use logits...
}];
Swift
let modelPath = Bundle.main.path(forResource: "mv3", ofType: "pte")!
// Create a module with the model file path. Nothing gets loaded into memory just yet.
let module = Module(filePath: modelPath)
// Force-load the program and 'forward' method. Otherwise, it's loaded at the first execution.
try module.load("forward")
let imageBuffer: UnsafeMutableRawPointer = ... // Existing image buffer
// Create an input tensor referencing the buffer and assuming the given shape and data type.
let inputTensor = Tensor(
bytesNoCopy: imageBuffer,
shape: [1, 3, 224, 224],
dataType: .float
)
// Execute the 'forward' method with the given input tensor and get output values back.
let outputs = try module.forward(inputTensor)
// Get the first output value assuming it's a tensor.
if let outputTensor = outputs.first?.tensor {
// Access the output tensor data.
outputTensor.bytes { pointer, count, dataType in
// Copy the tensor data into logits array for easier access.
let logits = Array(UnsafeBufferPointer(
start: pointer.assumingMemoryBound(to: Float.self),
count: count
))
// Use logits...
}
}
Tensor¶
Tensor 类(在 Objective-C 中公开为 ExecuTorchTensor
)表示一个多维元素数组(例如浮点数或整数),并包含 shape(维度)和数据类型等元数据。Tensor 用于向模型馈送输入和检索输出,或用于对原始数据进行任何计算。您可以从简单的数字数组创建 Tensor,检查它们的属性,读取或修改其内容,甚至可以重塑或复制它们。
关键属性:¶
dataType: 元素类型(例如,
.float
、.int
、.byte
)。shape: 描述每个维度大小的
NSNumber
数组。count: 元素总数。
strides: 沿每个维度前进一个元素所需的内存跳跃步长。
dimensionOrder: 内存中的维度顺序。
shapeDynamism: 指示 Tensor shape 是否可以更改(
.static
、.dynamicBound
、.dynamicUnbound
)。
初始化:¶
您可以通过多种方式创建 Tensor
从现有内存缓冲区创建
init(bytesNoCopy:shape:dataType:...)
: 创建一个引用现有内存缓冲区而不进行复制的 Tensor。缓冲区的生命周期必须超过 Tensor 的生命周期。init(bytes:shape:dataType:...)
: 通过从内存缓冲区复制数据创建 Tensor。
从 NSData
/ Data
创建
init(data:shape:dataType:...)
: 使用NSData
对象创建 Tensor,引用其字节而不进行复制。
从标量数组创建
init(_:shape:dataType:...)
: 从NSNumber
标量数组创建 Tensor。存在方便的初始化器,用于推断 shape 或数据类型。
从单个标量创建
init(_:)
,init(_:dataType:)
,init(float:)
,init(int:)
, 等:创建 0 维 Tensor(标量)。
Objective-C
#import <ExecuTorch/ExecuTorch.h>
// Create from copying bytes.
float data[] = {1.0f, 2.0f, 3.0f, 4.0f};
NSArray<NSNumber *> *shape = @[@2, @2];
ExecuTorchTensor *tensorFromBytes = [[ExecuTorchTensor alloc] initWithBytes:data
shape:shape
dataType:ExecuTorchDataTypeFloat];
// Create from scalars.
NSArray<NSNumber *> *scalars = @[@(1), @(2), @(3)];
ExecuTorchTensor *tensorFromScalars = [[ExecuTorchTensor alloc] initWithScalars:scalars
dataType:ExecuTorchDataTypeInt];
// Create a float scalar tensor.
ExecuTorchTensor *scalarTensor = [[ExecuTorchTensor alloc] initWithFloat:3.14f];
Swift
import ExecuTorch
// Create from existing buffer without copying.
var mutableData: [Float] = [1.0, 2.0, 3.0, 4.0]
let tensorNoCopy = mutableData.withUnsafeMutableBytes { bufferPointer in
Tensor(
bytesNoCopy: bufferPointer.baseAddress!,
shape: [2, 2],
dataType: .float
)
}
// Create from Data (no copy).
let data = Data(bytes: mutableData, count: mutableData.count * MemoryLayout<Float>.size)
let tensorFromData = Tensor(data: data, shape: [2, 2], dataType: .float)
// Create from scalars (infers float type).
let tensorFromScalars = Tensor([1.0, 2.0, 3.0, 4.0], shape: [4])
// Create an Int scalar tensor.
let scalarTensor = Tensor(42) // Infers Int as .long data type (64-bit integer)
访问数据:¶
使用 bytes(_:)
进行不可变访问,使用 mutableBytes(_:)
进行可变访问 Tensor 的底层数据缓冲区。
Objective-C
[tensor bytesWithHandler:^(const void *pointer, NSInteger count, ExecuTorchDataType dataType) {
if (dataType == ExecuTorchDataTypeFloat) {
const float *floatPtr = (const float *)pointer;
NSLog(@"First float element: %f", floatPtr[0]);
}
}];
[tensor mutableBytesWithHandler:^(void *pointer, NSInteger count, ExecuTorchDataType dataType) {
if (dataType == ExecuTorchDataTypeFloat) {
float *floatPtr = (float *)pointer;
floatPtr[0] = 100.0f; // Modify the original mutableData buffer.
}
}];
Swift
tensor.bytes { pointer, count, dataType in
if dataType == .float {
let buffer = UnsafeBufferPointer(start: pointer.assumingMemoryBound(to: Float.self), count: count)
print("First float element: \(buffer.first ?? 0.0)")
}
}
tensor.mutableBytes { pointer, count, dataType in
if dataType == .float {
let buffer = UnsafeMutableBufferPointer(start: pointer.assumingMemoryBound(to: Float.self), count: count)
buffer[1] = 200.0 // Modify the original mutableData buffer.
}
}
调整大小:¶
如果其底层内存分配允许(通常需要 Static 以外的 ShapeDynamism 或足够的容量),则可以调整 Tensor 的大小。
Objective-C
NSError *error;
BOOL success = [tensor resizeToShape:@[@4, @1] error:&error];
if (success) {
NSLog(@"Resized shape: %@", tensor.shape);
} else {
NSLog(@"Resize failed: %@", error);
}
Swift
do {
try tensor.resize(to: [4, 1])
print("Resized shape: \(tensor.shape)")
} catch {
print("Resize failed: \(error)")
}
Value¶
Value 类(在 Objective-C 中公开为 ExecuTorchValue
)是一个动态容器,可以容纳不同类型的数据,主要用于模型输入和输出。ExecuTorch 方法接受并返回 Value 对象数组。
关键属性:¶
tag
: 指示所持有的数据类型(例如,.tensor
、.integer
、.string
、.boolean
)。isTensor
,isInteger
,isString
, 等:用于检查类型的布尔属性。tensor
,integer
,string
,boolean
,double
: 访问底层数据的访问器(如果 tag 不匹配,则返回nil
或默认值)。
初始化:¶
直接从它们应持有的数据创建 Value 对象。
Objective-C
#import <ExecuTorch/ExecuTorch.h>
ExecuTorchTensor *tensor = [[ExecuTorchTensor alloc] initWithFloat:1.0f];
ExecuTorchValue *tensorValue = [[ExecuTorchValue alloc] valueWithTensor:tensor];
ExecuTorchValue *intValue = [[ExecuTorchValue alloc] valueWithInteger:100];
ExecuTorchValue *stringValue = [[ExecuTorchValue alloc] valueWithString:@"hello"];
ExecuTorchValue *boolValue = [[ExecuTorchValue alloc] valueWithBoolean:YES];
ExecuTorchValue *doubleValue = [[ExecuTorchValue alloc] valueWithDouble:3.14];
Swift
import ExecuTorch
let tensor = Tensor(2.0)
let tensorValue = Value(tensor)
let intValue = Value(200)
let stringValue = Value("world")
let boolValue = Value(false)
let doubleValue = Value(2.718)
Module¶
Module 类(在 Objective-C 中公开为 ExecuTorchModule
)表示一个已加载的 ExecuTorch 模型 (.pte
文件)。它提供方法来加载模型程序并执行其内部方法(例如 forward
)。
初始化:¶
通过提供 .pte
模型的文件路径创建 Module 实例。初始化本身是轻量级的,不会立即加载程序数据。
Objective-C
#import <ExecuTorch/ExecuTorch.h>
NSString *modelPath = [[NSBundle mainBundle] pathForResource:@"model" ofType:@"pte"];
ExecuTorchModule *module = [[ExecuTorchModule alloc] initWithFilePath:modelPath];
// Optional: specify load mode, e.g., memory mapping.
// ExecuTorchModule *moduleMmap = [[ExecuTorchModule alloc] initWithFilePath:modelPath
// loadMode:ExecuTorchModuleLoadModeMmap];
Swift
import ExecuTorch
let modelPath = Bundle.main.path(forResource: "model", ofType: "pte")
let module = Module(filePath: modelPath!)
// Optional: specify load mode, e.g., memory mapping.
// let moduleMmap = Module(filePath: modelPath, loadMode: .mmap)
加载:¶
模型加载会延迟到明确请求或需要执行时进行。虽然执行调用可以自动触发加载,但通常更有效的方式是提前显式加载方法。
load()
: 加载基本程序结构。默认使用最小验证。load(_:)
: 加载程序结构并准备特定方法(例如“forward”)以供执行。这会执行必要的设置,例如后端委托,如果您知道将运行哪个方法,建议使用此方法。isLoaded()
/isLoaded(_:)
: 检查加载状态。
Objective-C
NSError *error;
// Loads program and prepares 'forward' for execution.
BOOL success = [module loadMethod:@"forward" error:&error];
if (success) {
NSLog(@"Forward method loaded: %d", [module isMethodLoaded:@"forward"]);
} else {
NSLog(@"Failed to load method: %@", error);
}
Swift
do {
// Loads program and prepares 'forward' for execution.
try module.load("forward")
print("Forward method loaded: \(module.isLoaded("forward"))")
} catch {
print("Failed to load method: \(error)")
}
执行:¶
Module 类提供了灵活的方式来执行已加载程序中的方法。
命名执行:您可以使用
execute(methodName:inputs:)
按名称执行任何可用方法。Forward 快捷方式:对于运行主要推理方法的常见情况,请使用
forward(inputs:)
快捷方式,它等同于使用方法名称“forward”调用 execute。输入灵活性:输入可以通过多种方式提供
作为
Value
对象数组。这是最通用的形式。作为
Tensor
对象数组。这是一种便捷方式,其中 Tensor 会自动包装到Value
对象中。如果方法只期望一个输入,则作为单个
Value
或Tensor
对象。如果方法不接受输入,则不提供输入。
输出始终作为 Value
数组返回。
Objective-C
ExecuTorchTensor *inputTensor1 = [[ExecuTorchTensor alloc] initWithScalars:@[@1.0f, @2.0f]];
ExecuTorchTensor *inputTensor2 = [[ExecuTorchTensor alloc] initWithScalars:@[@3.0f, @4.0f]];
ExecuTorchTensor *singleInputTensor = [[ExecuTorchTensor alloc] initWithFloat:5.0f];
NSError *error;
// Execute "forward" using the shortcut with an array of Tensors.
NSArray<ExecuTorchValue *> *outputs1 = [module forwardWithTensors:@[inputTensor1, inputTensor2] error:&error];
if (outputs1) {
NSLog(@"Forward output count: %lu", (unsigned long)outputs1.count);
} else {
NSLog(@"Execution failed: %@", error);
}
// Execute "forward" with a single Tensor input.
NSArray<ExecuTorchValue *> *outputs2 = [module forwardWithTensor:singleInputTensor error:&error];
if (outputs2) {
NSLog(@"Forward single input output count: %lu", (unsigned long)outputs2.count);
} else {
NSLog(@"Execution failed: %@", error);
}
// Execute a potentially different method by name.
NSArray<ExecuTorchValue *> *outputs3 = [module executeMethod:@"another_method"
withInput:[[ExecuTorchValue alloc] valueWithTensor:inputTensor1]
error:&error];
// Process outputs (assuming first output is a tensor).
if (outputs1) {
ExecuTorchValue *firstOutput = outputs1.firstObject;
if (firstOutput.isTensor) {
ExecuTorchTensor *resultTensor = firstOutput.tensorValue;
// Process resultTensor.
}
}
Swift
let inputTensor1 = Tensor([1.0, 2.0], dataType: .float)
let inputTensor2 = Tensor([3.0, 4.0], dataType: .float)
let singleInputTensor = Tensor([5.0], dataType: .float)
do {
// Execute "forward" using the shortcut with an array of Tensors.
let outputs1 = try module.forward([inputTensor1, inputTensor2])
print("Forward output count: \(outputs1.count)")
// Execute "forward" with a single Tensor input.
let outputs2 = try module.forward(singleInputTensor)
print("Forward single input output count: \(outputs2.count)")
// Execute a potentially different method by name.
let outputs3 = try module.execute("another_method", inputs: [Value(inputTensor1)])
// Process outputs (assuming first output is a tensor).
if let resultTensor = outputs1.first?.tensor {
resultTensor.bytes { ptr, count, dtype in
// Access result data.
}
}
} catch {
print("Execution failed: \(error)")
}
方法名称:¶
程序加载后,您可以查询模型中可用的方法名称。
Objective-C
NSError *error;
// Note: methodNames: will load the program if not already loaded.
NSSet<NSString *> *names = [module methodNames:&error];
if (names) {
NSLog(@"Available methods: %@", names);
} else {
NSLog(@"Could not get method names: %@", error);
}
Swift
do {
// Note: methodNames() will load the program if not already loaded.
let names = try module.methodNames()
print("Available methods: \(names)") // Output: e.g., {"forward"}
} catch {
print("Could not get method names: \(error)")
}
日志记录¶
ExecuTorch 通过 ExecuTorchLog
(在 Swift 中为 Log
)单例提供用于 Objective-C 和 Swift 的日志记录 API。您可以订阅符合 ExecuTorchLogSink
(在 Swift 中为 LogSink
)协议的自定义日志接收器,以接收内部 ExecuTorch 日志消息。
注意:ExecuTorch 框架的 Release 版本构建会移除日志。要在开发期间捕获日志,请链接 Debug 构建版本(例如,executorch_debug
)。
Objective-C
#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 运行时链接为 Release 模式。如果需要调试,可以将 ExecuTorch 运行时保持在 Debug 模式,这不会对性能产生太大影响,但会保留日志记录和调试符号。
Swift PM¶
如果遇到 Swift PM 的校验和不匹配错误,请使用 Xcode 菜单(文件 > 包 > 重置包缓存
)或以下命令清除包缓存
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
注意:在运行终端命令之前,请确保 Xcode 已完全退出,以避免与活动进程冲突。