在 iOS 上使用 ExecuTorch¶
ExecuTorch 通过 Objective-C、Swift 和 C++ 支持 iOS 和 macOS。ExecuTorch 还提供后端,以利用 Core ML 和 Metal Performance Shaders (MPS) 在 Apple 平台上实现硬件加速执行。
集成¶
ExecuTorch for iOS 和 macOS 的运行时以预构建的 .xcframework 二进制目标集合形式分发。这些目标与 iOS 和 macOS 设备及模拟器兼容,并提供发布版和调试版两种模式。
executorch
- 主要运行时组件backend_coreml
- Core ML 后端backend_mps
- MPS 后端backend_xnnpack
- XNNPACK 后端kernels_custom
- LLM 的自定义内核kernels_optimized
- 优化内核kernels_portable
- 可移植内核(朴素实现用作参考)kernels_quantized
- 量化内核
将您的二进制文件与 ExecuTorch 运行时以及导出的 ML 模型使用的任何后端或内核链接。建议将核心运行时链接到直接使用 ExecuTorch 的组件,并将内核和后端链接到主应用目标。
注意: 要访问日志,请链接 ExecuTorch 运行时的调试版本,即 executorch_debug
框架。为获得最佳性能,请始终链接可交付成果的发布版本(不带 _debug
后缀的版本),这些版本已移除所有日志开销。
Swift 包管理器¶
预构建的 ExecuTorch 运行时、后端和内核可作为 Swift PM 包提供。
Xcode¶
在 Xcode 中,转到 File > Add Package Dependencies
。将 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+ 和命令行工具
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 Runtime 以及所有可用的内核和后端,支持发布版和调试版两种模式
./scripts/build_apple_frameworks.sh --Release --Debug --coreml --mps --xnnpack --custom --optimized --portable --quantized
构建成功完成后,生成的框架可以在 cmake-out
目录中找到。将它们复制到您的项目中并链接到您的目标。
链接¶
ExecuTorch 在应用启动期间通过在静态字典中注册后端和内核(运算符)来初始化它们。如果您在运行时遇到“未注册的内核”或“未注册的后端”之类的错误,您可能需要显式强制加载某些组件。在您的 Xcode 构建配置中使用 -all_load
或 -force_load
链接器标志,以确保组件被尽早注册。此外,您可能需要链接 C++ 标准库(如果尚未链接),例如通过添加 -lc++
链接器标志。
这是一个 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 运行时的调试版本(libexecutorch_debug
)以保留日志。通常,这不会对性能产生太大影响。尽管如此,请记住链接运行时的发布版本(libexecutorch
)以获得最佳性能和无日志。
您可以将此类配置文件分配给 Xcode 中的目标
将
.xcconfig
文件添加到您的项目中。导航到项目的 Info 选项卡。
在发布(或调试)模式的构建配置中选择配置文件。
运行时 API¶
ExecuTorch 提供了原生的 Objective-C API,这些 API 会自动桥接到 Swift,用于与运行时交互。这些 API 作为核心 C++ 组件的包装器,位于 extension/tensor 和 extension/module 中,为 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
)代表一个多维元素数组(如浮点数或整数),并包含像形状(维度)和数据类型这样的元数据。Tensor 用于将输入馈送到模型并检索输出,或用于您需要在原始数据上进行的任何计算。您可以从简单的数字数组创建 Tensor,检查它们的属性,读取或修改它们的内容,甚至重塑或复制它们。
关键属性:¶
dataType:元素类型(例如,
.float
、.int
、.byte
)。shape:一个
NSNumber
数组,描述每个维的大小。count:元素的总数。
strides:沿着每个维度移动一个元素所需的内存跳数。
dimensionOrder:内存中维度的顺序。
shapeDynamism:指示 Tensor 形状是否可以更改(
.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。存在方便的初始化器来推断形状或数据类型。
从单个标量
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.
}
}
调整大小:¶
如果 Tensor 的底层内存分配允许(通常需要 ShapeDynamism 不是 Static 或有足够容量),则可以调整 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 框架的发布版本中会移除日志。要捕获日志,请在开发过程中链接调试版本(例如 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。如果调用了正确的后端但性能问题仍然存在,请确认您链接的是后端运行时的发布版本。
为获得最佳性能,请将 ExecuTorch 运行时也链接到发布模式。如果需要调试,可以将 ExecuTorch 运行时保留在调试模式下,对性能影响最小,但保留日志和调试符号。
Swift 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
注意: 在运行终端命令之前,请确保 Xcode 已完全退出,以避免与活动进程发生冲突。