在 Apple 平台上整合及執行 ExecuTorch¶
適用於 iOS 和 macOS 的 ExecuTorch 執行階段以預先構建的 .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 執行階段的 Debug 組建,即 executorch_debug
framework。為了獲得最佳效能,請務必連結到交付項目的 Release 版本(沒有 _debug
後綴的版本),這些版本已移除所有日誌記錄的額外負荷。
整合¶
Swift Package Manager¶
預先建置的 ExecuTorch 執行階段、後端和核心可以作為 Swift PM 套件使用。
Xcode¶
在 Xcode 中,前往 File > Add Package Dependencies
。將 ExecuTorch repo 的 URL 貼到搜尋欄中並選取它。請務必將分支名稱變更為所需的 ExecuTorch 版本,格式為 "swiftpm-
然後選擇哪些 ExecuTorch framework 應該連結到哪個目標。
點擊下面的螢幕截圖,觀看示範影片,了解如何新增套件並在 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.5.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
Clone 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 二進位發佈版本,開啟 .dmg
檔案,將 CMake.app
移至 /Applications
目錄,然後執行以下指令以安裝 CMake 命令列工具
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
標誌附加到上述命令,以使用除錯符號建置二進位檔。
建置成功完成後,產生的 framework 可以在 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-$(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 建置配置,請將程式庫檔案名稱中的 release
替換為 debug
。請記住,即使其他元件是為 Release 建置的,也要在 Debug 模式下連結到 ExecuTorch 執行階段 (libexecutorch
),以便在需要時保留日誌。
您可以在 Xcode 中將此類配置檔案分配給您的目標
將
.xcconfig
檔案新增到您的專案。導覽至專案的 Info 標籤。
在 Release(或 Debug)模式的建置配置中選取配置檔案。
執行階段 API¶
請查看 C++ 執行階段 API 和 張量 教學課程,以了解更多關於如何載入和執行匯出的模型。建議對 macOS 或 iOS 使用 C++ API,並根據需要使用 Objective-C++ 和 Swift 程式碼進行封裝,以便將其公開給其他元件。請參考 Demo App 作為此類設定的範例。
一旦連結到 executorch
執行階段 framework,目標現在可以匯入所有 ExecuTorch 公用標頭。例如,在 Objective-C++ 中
#import <ExecuTorch/ExecuTorch.h>
#import <executorch/extension/module/module.h>
#import <executorch/extension/tensor/tensor.h>
或在 Swift 中
import ExecuTorch
注意: 匯入 ExecuTorch umbrella 標頭(或 Swift 中的 ExecuTorch 模組)僅提供對日誌記錄 API 的存取權。您仍然需要根據需要明確匯入其他執行階段標頭,例如 module.h
。除了下面描述的日誌記錄之外,Objective-C 或 Swift 中不支援其他執行階段 API。
注意: 日誌會在 ExecuTorch framework 的發佈版本中被刪除。為了保留日誌記錄,請在開發期間使用偵錯版本。
日誌記錄¶
我們為 Objective-C 和 Swift 中的日誌記錄提供額外的 API,作為內部 ExecuTorch 機制的輕量級包裝。若要使用它,只需在 Objective-C 中匯入主要 framework 標頭即可。然後使用 ExecuTorchLog
介面(或 Swift 中的 Log
類別)來訂閱您自己實作的 ExecuTorchLogSink
協定(或 Swift 中的 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 framework 的 Debug 建置,請設定您的偵錯器以使用偵錯會話中的以下 LLDB 指令正確對應原始碼
settings append target.source-map /executorch <path_to_executorch_source_code>
疑難排解¶
執行緩慢¶
請確認匯出的模型使用的是適當的後端,例如 XNNPACK、Core ML 或 MPS。如果已調用正確的後端,但效能問題仍然存在,請確認您連結的是後端執行階段的 Release 版本。
為了獲得最佳效能,請也以 Release 模式連結 ExecuTorch 執行階段。如果需要偵錯,您可以將 ExecuTorch 執行階段保持在 Debug 模式,對效能的影響最小,但保留記錄和偵錯符號。
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 已完全關閉,以避免與作用中的程序發生衝突。