Produrre codice NDK ottimizzato per più architetture?

Ho un codice C per Android che fa un sacco di crunch a basso livello. Mi piacerebbe sapere quali impostazioni dovrei usare (ad esempio per i miei file Android.mk e Application.mk) in modo che il codice prodotto venga eseguito su tutti gli attuali dispositivi Android, ma sfrutti anche le ottimizzazioni per chipset specifici. Sto cercando le impostazioni di default Android.mk e Application.mk da usare e voglio evitare di dover sparpagliare il mio codice C con le filiali #ifdef.

Ad esempio, sono a conoscenza del fatto che ARMv7 ha istruzioni in virgola mobile e alcuni chip ARMv7 supportano le istruzioni NEON e che l’ARM predefinito non supporta nessuno di questi. È ansible impostare i flag in modo che io possa creare ARMv7 con NEON, ARMv7 senza NEON e la build ARM predefinita? Sono sicuro di come fare gli ultimi due, ma non tutti 3. Sono cauto riguardo alle impostazioni che uso poiché presumo che le impostazioni predefinite correnti siano le impostazioni più sicure e quali siano le altre opzioni.

Per l’ottimizzazione specifica GCC, sto usando i seguenti flag:

LOCAL_CFLAGS=-ffast-math -O3 -funroll-loops 

Ho controllato tutti e 3 questi velocizzando il mio codice. Ci sono altri comuni che potrei aggiungere?

Un altro suggerimento che ho è quello di aggiungere “LOCAL_ARM_MODE: = arm” ad Android.mk per abilitare una maggiore velocità sui nuovi chip (anche se sono confuso esattamente su cosa fa e cosa succede sui chip più vecchi).

I processori ARM hanno 2 set di istruzioni generali che supportano: “ARM” e “Thumb”. Anche se ci sono diversi sapori di entrambi, le istruzioni ARM sono 32 bit ciascuna e le istruzioni Thumb sono 16 bit. La principale differenza tra i due è che le istruzioni ARM hanno la possibilità di fare di più in una singola istruzione rispetto alla lattina. Ad esempio, una singola istruzione ARM può aggiungere un registro a un altro registro, mentre si esegue uno spostamento a sinistra sul secondo registro. In Thumb un’istruzione dovrebbe fare il turno, quindi una seconda istruzione farebbe l’aggiunta.

Le istruzioni ARM non sono due volte più buone, ma in alcuni casi possono essere più veloci. Ciò è particolarmente vero nell’assemblaggio ARM arrotolato a mano, che può essere sintonizzato in nuovi modi per sfruttare al meglio gli “spostamenti gratuiti”. Le istruzioni del pollice hanno il loro vantaggio e dimensioni: scaricano la batteria di meno.

Ad ogni modo, questo è ciò che fa LOCAL_ARM_MODE – significa che compili il tuo codice come istruzioni ARM invece di istruzioni Thumb. Compiling to Thumb è l’impostazione predefinita nell’NDK in quanto tende a creare un binario più piccolo e la differenza di velocità non è così evidente per la maggior parte del codice. Il compilatore non può sempre trarre vantaggio dalla “mutazione” extra fornita da ARM, quindi si finisce per avere più o meno lo stesso numero di istruzioni comunque.

Il risultato di ciò che vedi dal codice C / C ++ compilato su ARM o Thumb sarà identico (salvo errori del compilatore ).

Questo di per sé è compatibile tra i processori ARM nuovi e vecchi per tutti i telefoni Android disponibili oggi. Ciò è dovuto al fatto che, per impostazione predefinita, NDK esegue la compilazione di una “Application Binary Interface” per le CPU basate su ARM che supportano il set di istruzioni ARMv5TE. Questo ABI è noto come “armeabi” e può essere impostato in modo esplicito in Application.mk inserendo APP_ABI := armeabi .

I processori più recenti supportano anche l’ABI specifico per Android noto come armeabi-v7a , che estende armeabi per aggiungere il set di istruzioni Thumb-2 e un set di istruzioni hardware a virgola mobile chiamato VFPv3-D16. Le CPU compatibili con armeabi-v7a possono anche supportare facoltativamente il set di istruzioni NEON, che è necessario verificare in fase di esecuzione e fornire percorsi di codice per quando è disponibile e quando non lo è. C’è un esempio nella directory NDK / samples che fa questo (ciao-neon). Sotto il cofano, Thumb-2 è più “simile ad ARM” in quanto le sue istruzioni possono fare di più in una singola istruzione, pur avendo il vantaggio di occupare ancora meno spazio.

Per compilare un “fat binary” che contiene entrambe le librerie armeabi e armeabi-v7a, devi aggiungere quanto segue a Application.mk:

 APP_ABI := armeabi armeabi-v7a 

Quando il file .apk è installato, il gestore di pacchetti Android installa la migliore libreria per il dispositivo. Quindi su piattaforms precedenti installerebbe la libreria armeabi e sui dispositivi più recenti quella armeabi-v7a.

Se si desidera verificare le funzionalità della CPU in fase di esecuzione, è ansible utilizzare la funzione NDK uint64_t android_getCpuFeatures() per ottenere le funzionalità supportate dal processore. Restituisce un bit-flag di ANDROID_CPU_ARM_FEATURE_ARMv7 sui processori v7a, ANDROID_CPU_ARM_FEATURE_VFPv3 se sono supportati i punti mobili hardware e ANDROID_CPU_ARM_FEATURE_NEON se sono supportate le istruzioni SIMD avanzate. ARM non può avere NEON senza VFPv3.

In sintesi: per impostazione predefinita, i programmi sono i più compatibili. L’uso di LOCAL_ARM_MODE potrebbe rendere le cose leggermente più veloci a scapito della durata della batteria grazie all’uso delle istruzioni ARM – ed è compatibile come l’impostazione predefinita. Aggiungendo la linea APP_ABI := armeabi armeabi-v7a avrai prestazioni migliorate su dispositivi più recenti, resterai compatibile con quelli più vecchi, ma il tuo file .apk sarà più grande (a causa di due librerie). Per utilizzare le istruzioni NEON, è necessario scrivere un codice speciale che rilevi le capacità della CPU in fase di esecuzione, e questo si applica solo ai nuovi dispositivi che possono eseguire armeabi-v7a.

Ottima risposta, proprio come aggiungere che dovresti usare

 APP_ABI := all 

questo compilerà 4 file binari, armv5, armv7, x86 e mips

potresti aver bisogno di una nuova versione di ndk