LiteRT bietet eine einheitliche Schnittstelle für die Verwendung von Neural Processing Units (NPUs), ohne dass Sie sich mit anbieterspezifischen Compilern, Laufzeiten oder Bibliotheksabhängigkeiten auseinandersetzen müssen. Die Verwendung von LiteRT für die NPU-Beschleunigung steigert die Leistung für Echtzeit- und Large-Model-Inferenz und minimiert Speicherkopien durch die Verwendung von Hardwarepuffern ohne Kopieren.
Jetzt starten
Eine Einführung finden Sie im NPU-Übersichtsleitfaden:
- Für klassische ML-Modelle finden Sie in den folgenden Abschnitten Informationen zu den Schritten für die Konvertierung, Kompilierung und Bereitstellung.
- Für Large Language Models (LLMs) verwenden Sie unser LiteRT-LM-Framework:
Beispielimplementierungen von LiteRT mit NPU-Unterstützung finden Sie in den folgenden Demo-Apps:
NPU-Anbieter
LiteRT unterstützt die NPU-Beschleunigung mit den folgenden Anbietern:
Qualcomm AI Engine Direct
- AOT- und On-Device-Kompilierungsausführungspfade werden über die Compiled Model API unterstützt.
- Weitere Informationen zur Einrichtung finden Sie unter Qualcomm AI Engine Direct.
MediaTek NeuroPilot
- AOT- und JIT-Ausführungspfade werden über die Compiled Model API unterstützt.
- Weitere Informationen zur Einrichtung finden Sie unter MediaTek NeuroPilot.
Modelle für die NPU konvertieren und kompilieren
Damit die NPU-Beschleunigung mit LiteRT verwendet werden kann, müssen Modelle in das LiteRT-Dateiformat konvertiert und für die Verwendung auf dem Gerät kompiliert werden. Mit dem LiteRT-AOT-Compiler (Ahead-of-Time) können Sie Modelle in ein KI-Paket kompilieren, in dem Ihre kompilierten Modelle mit Konfigurationen für die Ausrichtung auf Geräte gebündelt werden. So wird überprüft, ob Modelle auf Geräten korrekt bereitgestellt werden, je nachdem, ob sie für bestimmte SoCs ausgestattet oder optimiert sind.
Nachdem Sie die Modelle konvertiert und kompiliert haben, können Sie Play for On-device AI (PODAI) verwenden, um Modelle bei Google Play hochzuladen und über das On-Demand AI-Framework auf Geräte zu übertragen.
Notebook zur AOT-Kompilierung von LiteRT
[AOT only] Mit Play AI Pack bereitstellen
Nachdem Sie das Modell konvertiert und ein KI-Paket kompiliert haben, führen Sie die folgenden Schritte aus, um das KI-Paket bei Google Play bereitzustellen.
KI-Pakete in das Gradle-Projekt importieren
Kopieren Sie die KI-Pakete in das Stammverzeichnis des Gradle-Projekts. Beispiel:
my_app/
...
ai_packs/
my_model/...
my_model_mtk/...
Fügen Sie jedes AI Pack der Gradle-Build-Konfiguration hinzu:
// my_app/ai_packs/my_model/build.gradle.kts
plugins { id("com.android.ai-pack") }
aiPack {
packName = "my_model" // ai pack dir name
dynamicDelivery { deliveryType = "on-demand" }
}
// Add another build.gradle.kts for my_model_mtk/ as well
NPU-Laufzeitbibliotheken zum Projekt hinzufügen
Laden Sie litert_npu_runtime_libraries.zip für AOT oder litert_npu_runtime_libraries_jit.zip für JIT herunter und entpacken Sie die Datei im Stammverzeichnis des Projekts:
my_app/
...
litert_npu_runtime_libraries/
mediatek_runtime/...
qualcomm_runtime_v69/...
qualcomm_runtime_v73/...
qualcomm_runtime_v75/...
qualcomm_runtime_v79/...
qualcomm_runtime_v81/...
fetch_qualcomm_library.sh
Führen Sie das Skript aus, um die NPU-Supportbibliotheken herunterzuladen. Führen Sie beispielsweise Folgendes für Qualcomm-NPUs aus:
$ ./litert_npu_runtime_libraries/fetch_qualcomm_library.sh
AI Packs und NPU-Laufzeitbibliotheken zur Gradle-Konfiguration hinzufügen
Kopieren Sie device_targeting_configuration.xml aus den generierten KI-Paketen in das Verzeichnis des Haupt-App-Moduls. Aktualisieren Sie dann settings.gradle.kts:
// my_app/setting.gradle.kts
...
// [AOT only]
// AI Packs
include(":ai_packs:my_model")
include(":ai_packs:my_model_mtk")
// NPU runtime libraries
include(":litert_npu_runtime_libraries:runtime_strings")
include(":litert_npu_runtime_libraries:mediatek_runtime")
include(":litert_npu_runtime_libraries:qualcomm_runtime_v69")
include(":litert_npu_runtime_libraries:qualcomm_runtime_v73")
include(":litert_npu_runtime_libraries:qualcomm_runtime_v75")
include(":litert_npu_runtime_libraries:qualcomm_runtime_v79")
include(":litert_npu_runtime_libraries:qualcomm_runtime_v81")
Aktualisieren Sie build.gradle.kts:
// my_app/build.gradle.kts
android {
...
defaultConfig {
...
// API level 31+ is required for NPU support.
minSdk = 31
// NPU only supports arm64-v8a
ndk { abiFilters.add("arm64-v8a") }
// Needed for Qualcomm NPU runtime libraries
packaging { jniLibs { useLegacyPackaging = true } }
}
// Device targeting
bundle {
deviceTargetingConfig = file("device_targeting_configuration.xml")
deviceGroup {
enableSplit = true // split bundle by #group
defaultGroup = "other" // group used for standalone APKs
}
}
// [AOT Only]
// AI Packs
assetPacks.add(":ai_packs:my_model")
assetPacks.add(":ai_packs:my_model_mtk")
// NPU runtime libraries
dynamicFeatures.add(":litert_npu_runtime_libraries:mediatek_runtime")
dynamicFeatures.add(":litert_npu_runtime_libraries:qualcomm_runtime_v69")
dynamicFeatures.add(":litert_npu_runtime_libraries:qualcomm_runtime_v73")
dynamicFeatures.add(":litert_npu_runtime_libraries:qualcomm_runtime_v75")
dynamicFeatures.add(":litert_npu_runtime_libraries:qualcomm_runtime_v79")
dynamicFeatures.add(":litert_npu_runtime_libraries:qualcomm_runtime_v81")
}
dependencies {
// Dependencies for strings used in the runtime library modules.
implementation(project(":litert_npu_runtime_libraries:runtime_strings"))
...
}
[Nur AOT] On-Demand-Bereitstellung verwenden
Wenn die Funktion „Android AI Pack“ in der Datei build.gradle.kts konfiguriert ist, prüfen Sie die Gerätefunktionen und verwenden Sie die NPU auf kompatiblen Geräten. Verwenden Sie GPU und CPU als Fallback:
val env = Environment.create(BuiltinNpuAcceleratorProvider(context))
val modelProvider = AiPackModelProvider(
context, "my_model", "model/my_model.tflite") {
if (NpuCompatibilityChecker.Qualcomm.isDeviceSupported())
setOf(Accelerator.NPU) else setOf(Accelerator.CPU, Accelerator.GPU)
}
val mtkModelProvider = AiPackModelProvider(
context, "my_model_mtk", "model/my_model_mtk.tflite") {
if (NpuCompatibilityChecker.Mediatek.isDeviceSupported())
setOf(Accelerator.NPU) else setOf()
}
val modelSelector = ModelSelector(modelProvider, mtkModelProvider)
val model = modelSelector.selectModel(env)
val compiledModel = CompiledModel.create(
model.getPath(),
CompiledModel.Options(model.getCompatibleAccelerators()),
env,
)
CompiledModel für den JIT-Modus erstellen
val env = Environment.create(BuiltinNpuAcceleratorProvider(context))
val compiledModel = CompiledModel.create(
"model/my_model.tflite",
CompiledModel.Options(Accelerator.NPU),
env,
)
Inferenz auf der NPU mit LiteRT in Kotlin
Wenn Sie den NPU-Beschleuniger verwenden möchten, übergeben Sie beim Erstellen des kompilierten Modells (CompiledModel) den NPU-Parameter.
Das folgende Code-Snippet zeigt eine einfache Implementierung des gesamten Prozesses in Kotlin:
val inputBuffers = model.createInputBuffers()
val outputBuffers = model.createOutputBuffers()
inputBuffers[0].writeFloat(FloatArray(data_size) { data_value })
model.run(inputBuffers, outputBuffers)
val outputFloatArray = outputBuffers[0].readFloat()
inputBuffers.forEach { it.close() }
outputBuffers.forEach { it.close() }
model.close()
Inferenz auf der NPU mit LiteRT in C++
Build-Abhängigkeiten
C++-Nutzer müssen die Abhängigkeiten der Anwendung mit LiteRT-NPU-Beschleunigung erstellen. Die cc_binary-Regel, die die Kernanwendungslogik (z.B. main.cc) sind die folgenden Laufzeitkomponenten erforderlich:
- Gemeinsame Bibliothek der LiteRT C API: Das Attribut
datamuss die gemeinsame Bibliothek der LiteRT C API (//litert/c:litert_runtime_c_api_shared_lib) und das anbieterspezifische Dispatch-Shared-Object für die NPU (//litert/vendors/qualcomm/dispatch:dispatch_api_so) enthalten. - NPU-spezifische Backend-Bibliotheken: Zum Beispiel die Qualcomm AI RT (QAIRT)-Bibliotheken für den Android-Host (z. B.
libQnnHtp.so,libQnnHtpPrepare.so) und die entsprechende Hexagon DSP-Bibliothek (libQnnHtpV79Skel.so). So kann die LiteRT-Laufzeit Berechnungen an die NPU auslagern. - Attributabhängigkeiten: Das Attribut
depsverweist auf wichtige Compile-Zeit-Abhängigkeiten wie den Tensorpuffer von LiteRT (//litert/cc:litert_tensor_buffer) und die API für die NPU-Dispatch-Ebene (//litert/vendors/qualcomm/dispatch:dispatch_api). Dadurch kann Ihr Anwendungscode über LiteRT mit der NPU interagieren. - Modelldateien und andere Assets: Über das Attribut
dataeingebunden.
Mit dieser Einrichtung kann Ihre kompilierte Binärdatei die NPU dynamisch laden und für die beschleunigte Inferenz von maschinellem Lernen verwenden.
NPU-Umgebung einrichten
Für einige NPU-Back-Ends sind Laufzeitabhängigkeiten oder Bibliotheken erforderlich. Bei Verwendung der API für kompilierte Modelle werden diese Anforderungen von LiteRT über ein Environment-Objekt organisiert.
Mit dem folgenden Code können Sie die entsprechenden NPU-Bibliotheken oder ‑Treiber finden:
// Provide a dispatch library directory (following is a hypothetical path) for the NPU
std::vector<Environment::Option> environment_options = {
{
Environment::OptionTag::DispatchLibraryDir,
"/usr/lib64/npu_dispatch/"
}
};
LITERT_ASSIGN_OR_RETURN(auto env, Environment::Create(absl::MakeConstSpan(environment_options)));
Laufzeitintegration
Das folgende Code-Snippet zeigt eine einfache Implementierung des gesamten Prozesses in C++:
// 1. Load the model that has NPU-compatible ops
LITERT_ASSIGN_OR_RETURN(auto model, Model::Load("mymodel_npu.tflite"));
// 2. Create a compiled model with NPU acceleration
// See following section on how to set up NPU environment
LITERT_ASSIGN_OR_RETURN(auto compiled_model,
CompiledModel::Create(env, model, kLiteRtHwAcceleratorNpu));
// 3. Allocate I/O buffers
LITERT_ASSIGN_OR_RETURN(auto input_buffers, compiled_model.CreateInputBuffers());
LITERT_ASSIGN_OR_RETURN(auto output_buffers, compiled_model.CreateOutputBuffers());
// 4. Fill model inputs (CPU array -> NPU buffers)
float input_data[] = { /* your input data */ };
input_buffers[0].Write<float>(absl::MakeConstSpan(input_data, /*size*/));
// 5. Run inference
compiled_model.Run(input_buffers, output_buffers);
// 6. Access model output
std::vector<float> data(output_data_size);
output_buffers[0].Read<float>(absl::MakeSpan(data));
Zero-Copy mit NPU-Beschleunigung
Durch die Verwendung von Zero-Copy kann eine NPU direkt auf Daten im eigenen Speicher zugreifen, ohne dass die CPU diese Daten explizit kopieren muss. Da keine Daten in den und aus dem CPU-Arbeitsspeicher kopiert werden müssen, kann die End-to-End-Latenz durch Zero-Copy deutlich reduziert werden.
Der folgende Code ist eine Beispielimplementierung von Zero-Copy-NPU mit AHardwareBuffer, bei der Daten direkt an die NPU übergeben werden. Durch diese Implementierung werden teure Roundtrips zum CPU-Speicher vermieden, wodurch der Inferenz-Overhead erheblich reduziert wird.
// Suppose you have AHardwareBuffer* ahw_buffer
LITERT_ASSIGN_OR_RETURN(auto tensor_type, model.GetInputTensorType("input_tensor"));
LITERT_ASSIGN_OR_RETURN(auto npu_input_buffer, TensorBuffer::CreateFromAhwb(
env,
tensor_type,
ahw_buffer,
/* offset = */ 0
));
std::vector<TensorBuffer> input_buffers{npu_input_buffer};
LITERT_ASSIGN_OR_RETURN(auto output_buffers, compiled_model.CreateOutputBuffers());
// Execute the model
compiled_model.Run(input_buffers, output_buffers);
// Retrieve the output (possibly also an AHWB or other specialized buffer)
auto ahwb_output = output_buffers[0].GetAhwb();
Mehrere NPU-Inferenzvorgänge verketten
Bei komplexen Pipelines können Sie mehrere NPU-Inferenzvorgänge verketten. Da in jedem Schritt ein accelerator-freundlicher Puffer verwendet wird, bleibt Ihre Pipeline größtenteils im NPU-verwalteten Speicher:
// compiled_model1 outputs into an AHWB
compiled_model1.Run(input_buffers, intermediate_buffers);
// compiled_model2 consumes that same AHWB
compiled_model2.Run(intermediate_buffers, final_outputs);
Just-in-time-Kompilierungs-Caching für NPU
LiteRT unterstützt die NPU-Just-in-Time-Kompilierung (JIT) von .tflite-Modellen. Die JIT-Kompilierung kann besonders nützlich sein, wenn es nicht möglich ist, das Modell vorab zu kompilieren.
Die JIT-Kompilierung kann jedoch zu Wartezeiten und Speicher-Overhead führen, da das vom Nutzer bereitgestellte Modell bei Bedarf in NPU-Bytecode-Anweisungen übersetzt werden muss. Um die Auswirkungen auf die Leistung zu minimieren, können NPU-Kompilierungsartefakte im Cache gespeichert werden.
Wenn das Caching aktiviert ist, löst LiteRT die Neukompilierung des Modells nur bei Bedarf aus, z.B.:
- Die Version des NPU-Compiler-Plug-ins des Anbieters hat sich geändert.
- Der Android-Build-Fingerprint hat sich geändert.
- Das vom Nutzer bereitgestellte Modell wurde geändert.
- Die Kompilierungsoptionen wurden geändert.
Wenn Sie das NPU-Kompilierungs-Caching aktivieren möchten, geben Sie in den Umgebungsoptionen das Umgebungstag CompilerCacheDir an. Der Wert muss auf einen vorhandenen beschreibbaren Pfad der Anwendung festgelegt werden.
const std::array environment_options = {
litert::Environment::Option{
/*.tag=*/litert::Environment::OptionTag::CompilerPluginLibraryDir,
/*.value=*/kCompilerPluginLibSearchPath,
},
litert::Environment::Option{
litert::Environment::OptionTag::DispatchLibraryDir,
kDispatchLibraryDir,
},
// 'kCompilerCacheDir' will be used to store NPU-compiled model
// artifacts.
litert::Environment::Option{
litert::Environment::OptionTag::CompilerCacheDir,
kCompilerCacheDir,
},
};
// Create an environment.
LITERT_ASSERT_OK_AND_ASSIGN(
auto environment, litert::Environment::Create(environment_options));
// Load a model.
auto model_path = litert::testing::GetTestFilePath(kModelFileName);
LITERT_ASSERT_OK_AND_ASSIGN(auto model,
litert::Model::CreateFromFile(model_path));
// Create a compiled model, which only triggers NPU compilation if
// required.
LITERT_ASSERT_OK_AND_ASSIGN(
auto compiled_model, litert::CompiledModel::Create(
environment, model, kLiteRtHwAcceleratorNpu));
Beispiel für Latenz und Arbeitsspeichereinsparungen:
Die für die NPU-Kompilierung erforderliche Zeit und der erforderliche Arbeitsspeicher können je nach verschiedenen Faktoren variieren, z. B. dem zugrunde liegenden NPU-Chip und der Komplexität des Eingabemodells.
In der folgenden Tabelle werden die Laufzeitinitialisierungszeit und der Arbeitsspeicherverbrauch verglichen, wenn eine NPU-Kompilierung erforderlich ist und wenn die Kompilierung aufgrund von Caching übersprungen werden kann. Auf einem Beispielgerät erhalten wir Folgendes:
| TFLite-Modell | Modellinitialisierung mit NPU-Kompilierung | Modellinitialisierung mit zwischengespeicherter Kompilierung | Speicherbedarf bei der Initialisierung mit NPU-Kompilierung | Arbeitsspeicher mit zwischengespeicherter Kompilierung initialisieren |
|---|---|---|---|---|
| torchvision_resnet152.tflite | 7465,22 ms | 198,34 ms | 1525,24 MB | 355,07 MB |
| torchvision_lraspp_mobilenet_v3_large.tflite | 1592,54 ms | 166,47 ms | 254,90 MB | 33,78 MB |
Auf einem anderen Gerät erhalten wir Folgendes:
| TFLite-Modell | Modellinitialisierung mit NPU-Kompilierung | Modellinitialisierung mit zwischengespeicherter Kompilierung | Speicherbedarf bei der Initialisierung mit NPU-Kompilierung | Arbeitsspeicher mit zwischengespeicherter Kompilierung initialisieren |
|---|---|---|---|---|
| torchvision_resnet152.tflite | 2766,44 ms | 379,86 ms | 653,54 MB | 501,21 MB |
| torchvision_lraspp_mobilenet_v3_large.tflite | 784,14 ms | 231,76 ms | 113,14 MB | 67,49 MB |