53 votes

Produire du code NDK optimisé pour plusieurs architectures ?

J'ai du code C pour Android qui fait beaucoup de calculs de bas niveau. J'aimerais savoir quels paramètres je devrais utiliser (par exemple pour mes fichiers Android.mk et Application.mk) pour que le code produit fonctionne sur tous les appareils Android actuels, tout en tirant parti des optimisations pour les chipsets spécifiques. Je cherche de bons paramètres par défaut à utiliser pour Android.mk et Application.mk et je veux éviter d'avoir à joncher mon code C avec des branches #ifdef.

Par exemple, je sais que l'ARMv7 possède des instructions en virgule flottante et que certaines puces ARMv7 prennent en charge les instructions NEON, mais que l'ARM par défaut ne prend en charge ni l'une ni l'autre de ces instructions. Est-il possible de définir des drapeaux afin de pouvoir construire ARMv7 avec NEON, ARMv7 sans NEON et la construction ARM par défaut ? Je sais comment faire les deux derniers mais pas les 3. Je suis prudent quant aux paramètres que j'utilise car je suppose que les paramètres par défaut actuels sont les plus sûrs et je connais les risques des autres options.

Pour l'optimisation spécifique à GCC, j'utilise les drapeaux suivants :

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

J'ai vérifié que ces trois éléments accélèrent mon code. Y a-t-il d'autres éléments courants que je pourrais ajouter ?

Une autre astuce consiste à ajouter "LOCAL_ARM_MODE := arm" à Android.mk pour accélérer la vitesse sur les nouvelles puces arm (bien que je ne sache pas exactement ce que cela fait et ce qui se passe sur les puces plus anciennes).

112voto

richq Points 29694

Les processeurs ARM prennent en charge deux jeux d'instructions générales : "ARM" et "Thumb". Bien qu'il existe différentes variantes des deux, les instructions ARM sont de 32 bits chacune et les instructions Thumb de 16 bits. La principale différence entre les deux est que les instructions ARM ont la possibilité de faire plus en une seule instruction que les instructions Thumb. Par exemple, une seule instruction ARM peut ajouter un registre à un autre registre, tout en effectuant un décalage vers la gauche sur le second registre. Dans le Thumb, une instruction devrait effectuer le décalage, puis une seconde instruction effectuerait l'addition.

Les instructions ARM ne sont pas deux fois plus performantes, mais dans certains cas, elles peuvent être plus rapides. C'est particulièrement vrai pour l'assemblage ARM contrôlé à la main, qui peut être réglé de manière originale pour tirer le meilleur parti des "décalages gratuits". Les instructions Thumb ont leur propre avantage, en plus de leur taille : elles épuisent moins la batterie.

C'est ce que fait LOCAL_ARM_MODE - cela signifie que vous compilez votre code avec des instructions ARM au lieu d'instructions Thumb. La compilation en mode Thumb est la valeur par défaut du NDK, car elle tend à créer un binaire plus petit et la différence de vitesse n'est pas très perceptible pour la plupart des codes. Le compilateur ne peut pas toujours tirer parti de la puissance supplémentaire que l'ARM peut fournir, de sorte que vous finissez par avoir besoin plus ou moins du même nombre d'instructions de toute façon.

Le résultat du code C/C++ compilé pour ARM ou Thumb sera identique (à l'exception de bogues du compilateur ).

En soi, ce système est compatible avec les nouveaux et les anciens processeurs ARM de tous les téléphones Android disponibles aujourd'hui. En effet, le NDK compile par défaut une "interface binaire d'application" pour les processeurs ARM qui prennent en charge le jeu d'instructions ARMv5TE. Cette ABI est connue sous le nom de "armeabi" et peut être explicitement définie dans le fichier Application.mk en mettant APP_ABI := armeabi .

Les processeurs plus récents prennent également en charge l'ABI spécifique à Android, connue sous le nom de armeabi-v7a qui étend armeabi pour y ajouter le Jeu d'instructions Thumb-2 et un jeu d'instructions matériel en virgule flottante appelé VFPv3-D16. Les CPU compatibles avec l'armeabi-v7a peuvent également supporter de manière optionnelle le jeu d'instructions NEON, que vous devez vérifier au moment de l'exécution et fournir des chemins de code lorsqu'il est disponible et lorsqu'il ne l'est pas. Il y a un exemple dans le répertoire NDK/samples qui fait cela (hello-neon). Sous le capot, Thumb-2 est plus "ARM-like" dans la mesure où ses instructions peuvent faire plus en une seule instruction, tout en ayant l'avantage de prendre moins d'espace.

Pour compiler un "fat binary" qui contient à la fois les bibliothèques armeabi et armeabi-v7a, il faut ajouter ce qui suit au fichier Application.mk :

APP_ABI := armeabi armeabi-v7a

Lorsque le fichier .apk est installé, le gestionnaire de paquets d'Android installe la meilleure bibliothèque pour l'appareil. Ainsi, sur les anciennes plateformes, il installe la bibliothèque armeabi, et sur les appareils plus récents, la bibliothèque armeabi-v7a.

Si vous souhaitez tester les caractéristiques du processeur au moment de l'exécution, vous pouvez utiliser la fonction NDK uint64_t android_getCpuFeatures() pour obtenir les fonctionnalités prises en charge par le processeur. Cette fonction renvoie un drapeau binaire de ANDROID_CPU_ARM_FEATURE_ARMv7 sur les processeurs v7a, ANDROID_CPU_ARM_FEATURE_VFPv3 si les points flottants matériels sont pris en charge et ANDROID_CPU_ARM_FEATURE_NEON si des instructions SIMD avancées sont prises en charge. ARM ne peut pas avoir NEON sans VFPv3.

En résumé : par défaut, vos programmes sont les plus compatibles. L'utilisation de LOCAL_ARM_MODE peut rendre les choses légèrement plus rapides au détriment de la durée de vie de la batterie en raison de l'utilisation d'instructions ARM - et elle est aussi compatible que la configuration par défaut. En ajoutant l'option APP_ABI := armeabi armeabi-v7a Vous obtiendrez de meilleures performances sur les nouveaux appareils, vous resterez compatible avec les anciens, mais votre fichier .apk sera plus volumineux (en raison de la présence de deux bibliothèques). Afin d'utiliser les instructions NEON, vous devrez écrire un code spécial qui détecte les capacités du CPU au moment de l'exécution, et cela ne s'applique qu'aux nouveaux appareils qui peuvent exécuter l'armeabi-v7a.

23voto

user1159819 Points 547

Excellente réponse, mais j'aimerais ajouter que vous devriez utiliser

APP_ABI := all

Ceci compilera 4 binaires, armv5, armv7, x86 et mips.

il se peut que vous ayez besoin d'une nouvelle version de ndk

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X