NPU Manager

Android 17 e versioni successive supportano NPU Manager (com.android.npumanager), che coordina l'allocazione e la pianificazione delle risorse NPU tra i servizi di sistema e i carichi di lavoro delle applicazioni. Spostando l'arbitraggio delle risorse dai daemon personalizzati del fornitore alla piattaforma Android, NPU Manager aumenta la prevedibilità, impedisce la carenza di risorse, gestisce i limiti termici e migliora le prestazioni complessive del dispositivo.

Background e motivazione

Prima di NPU Manager, le app e i moduli di sistema inviavano i carichi di lavoro direttamente ai driver del fornitore o ai servizi proprietari. Questo approccio presentava diversi svantaggi:

  • Competizione inefficiente per le risorse: i carichi di lavoro di machine learning pesanti (come i motori di inferenza dei modelli linguistici di grandi dimensioni (LLM) o i sistemi di visione on-device) competevano direttamente con altri sistemi ad alta priorità per le risorse NPU finite (come SRAM, memoria dei pesi e canali di esecuzione).
  • Instabilità del sistema: i carichi di lavoro non coordinati potevano attivare la limitazione termica, gli errori di pagina di memoria o il daemon LMKD (Low Memory Killer) se le richieste superavano la capacità hardware.
  • Prioritizzazione inefficiente: il server di sistema non può regolare la priorità NPU in risposta ai cambiamenti di contesto, ad esempio un'attività in background che carica un modello di grandi dimensioni mentre in primo piano è attivo un pipeline della fotocamera sensibile alla latenza o un assistente utente.

NPU Manager risolve queste sfide fungendo da arbitro a livello di sistema che controlla il caricamento dei modelli e regola dinamicamente le priorità di esecuzione in base all'integrità attuale del dispositivo e agli stati delle app.

Architettura di sistema

NPU Manager viene implementato come servizio di sistema denominato npu in esecuzione all'interno del framework Android. NPU Manager isola la coordinazione di alto livello delle norme di pianificazione dall'implementazione del driver del fornitore di basso livello.

Il seguente diagramma illustra i layer dell'ambiente NPU Manager:

Livelli dell'ambiente NPU Manager

Figura 1. Layer dell'ambiente NPU Manager.

Componenti chiave

  • Client API del framework (android.npumanager.NpuManager): il punto di ingresso utilizzato dai client per richiedere le prenotazioni di caricamento dei modelli.
  • Servizio di sistema (npu): un servizio di sistema che controlla le approvazioni di caricamento dei modelli e gestisce i comandi di prerilascio in base alle regole di priorità di pianificazione.
  • HAL di pianificazione NPU (android.hardware.npu): un'interfaccia basata su AIDL che inoltra i callback delle priorità delle app per Android tra il framework e il driver.
  • Driver del fornitore: un driver di basso livello che controlla i blocchi di esecuzione hardware e implementa meccanismi di prioritizzazione di basso livello.

API SDK e framework

Prima di chiamare le librerie di reti neurali di basso livello o caricare i file dei modelli, i client del framework devono interagire con il servizio NpuManager. Per farlo, i client definiscono innanzitutto una richiesta di caricamento del modello, quindi eseguono il flusso di richiesta e approvazione.

Richiesta di caricamento del modello

Una richiesta di caricamento del modello è rappresentata da ModelLoadRequest. Questo oggetto contiene:

  • ID richiesta univoco
  • Classe di dimensioni del modello stimata, ad esempio NPU_MODEL_SIZE_LESS_THAN_1GB o NPU_MODEL_SIZE_GREATER_THAN_2G
  • Priorità prevista, ad esempio NPU_MODEL_PRIORITY_BACKGROUND, NPU_MODEL_PRIORITY_NORMAL o NPU_MODEL_PRIORITY_OPPORTUNISTIC

Il seguente esempio di codice crea un ModelLoadRequest con un limite di dimensioni superiore a 2 GB e una priorità di esecuzione normale:

ModelLoadRequest request = new ModelLoadRequest.Builder(requestId)
        .setSize(NPU_MODEL_SIZE_GREATER_THAN_2G)
        .setPriority(NPU_MODEL_PRIORITY_NORMAL)
        .build();

Flusso di richiesta e approvazione

I client richiamano requestCanLoadModel in modo asincrono:

npuManager.requestCanLoadModel(request, callback, executor);

Quando le risorse NPU sono disponibili, il framework risponde utilizzando ModelLoadRequestCallback con i seguenti eventi:

  • onCanLoadModel(request, status, listener): attivato quando la richiesta viene approvata. Il client riceve un token NpuManager.ModelLoadStatusListener. Dopo che il client ha caricato completamente il modello nella memoria del driver, deve chiamare listener.notifyModelLoaded(request).
  • onRequestUnloadModel(request) o onRequestUnloadModel(request, reason): attivato quando il sistema riscontra una pressione sulle risorse (ad esempio una richiesta in primo piano in arrivo o un picco termico) e richiede al client di rilasciare il modello. Dopo aver recuperato le risorse NPU, il client chiama listener.notifyModelUnloaded(request).
  • onModelLoadRequestComplete(request, status): informa il client delle modifiche finali del ciclo di vita della richiesta, ad esempio l'annullamento.

I client possono annullare gli inviti in attesa utilizzando cancelModelLoad(request).

Integrazione HAL e fornitore

Per supportare NPU Manager, le implementazioni specifiche del dispositivo del fornitore devono essere conformi alle interfacce di servizio AIDL android.hardware.npu.

Configurazione della pianificazione

Il sistema inoltra la priorità dell'app utilizzando la struttura AIDL SchedulingConfig AIDL definita in IScheduling.aidl:SchedulingConfig

package android.hardware.npu;

@VintfStability
parcelable SchedulingConfig {
    int minPriority;
    int maxPriority;
    int uid;
    int appPriority;
    boolean hasDirectAccess;
    boolean canAttributeOtherUid;
}

Utilizzando questa struttura, NPU Manager coordina gli allineamenti delle priorità. Ad esempio, se un'app in background invia un job ad alta priorità, la priorità viene ridotta per evitare interferenze con la grafica in primo piano.

Stato e profilazione delle attività

I driver del fornitore devono segnalare lo stato del ciclo di vita dei gruppi di esecuzione NPU al gestore. WorkInfo tiene traccia delle attività (definite in WorkInfo.aidl):

package android.hardware.npu;

import android.hardware.npu.NpuUuid;

@VintfStability
parcelable WorkInfo {
    int id;
    @nullable NpuUuid groupId;
    int uid;
    int debugPid;
    int originalUid;
    @nullable String debugFeatureId;
    int jobPriority;
    int effectivePriority;
    long timestampMs;
    int deviceNumber;
}

Debouncing degli eventi

Il framework di pianificazione supporta il debouncing degli eventi utilizzando il parametro debounce_duration_ms nella registrazione del callback di pianificazione. In questo modo si evita il flooding dei log e si eliminano le notifiche rapide, ad esempio gli eventi di inizio e fine consecutivi per i modelli ripetuti.

Gli stati del ciclo di vita dei callback vengono segnalati come segue:

  • onWorkRequested: il carico di lavoro viene messo in coda dal servizio del fornitore.
  • onWorkStarted: inizia l'esecuzione del carico di lavoro.
    • NPU_START_REASON_INITIAL: prima esecuzione.
    • NPU_START_REASON_RESUMED: esecuzione ripresa dopo il prerilascio.
  • onWorkEnded: l'esecuzione del carico di lavoro è terminata.
    • NPU_END_REASON_COMPLETED: completamento dell'esecuzione riuscito.
    • NPU_END_REASON_CANCELLED_USER: annullato dal client.
    • NPU_END_REASON_CANCELLED_SYSTEM: prerilasciato dalla policy di sistema.
    • NPU_END_REASON_FAILED: errore di esecuzione o errore del driver.
    • NPU_END_REASON_PAUSED: sospeso temporaneamente per le attività ad alta priorità.

Preparazione e test del dispositivo

Assicurati che queste configurazioni siano attive prima di verificare l'integrità del dispositivo.

Dichiarazioni delle applicazioni

I client che richiedono la prioritizzazione della pianificazione NPU devono dichiarare la funzionalità hardware NPU nel file AndroidManifest.xml:

<uses-feature android:name="android.hardware.npu" android:required="false" />

Per i modelli di cui è stato eseguito il deployment su generazioni più recenti di hardware partner, questa dichiarazione potrebbe essere necessaria per la creazione ottimale del motore.

Test di integrazione VTS

Le implementazioni HAL NPU possono essere convalidate con i test funzionali VTS, ad esempio VtsHalNpuSchedulingTargetTest.