Comment améliorer le support X86 des applications et bibliothèques sur Android

Xavier HALLADE

Puisque c'est mon premier article ici, je vous dois une introduction : je suis ingénieur logiciel chez Intel France, et une partie de mon travail consiste à aider les développeurs à améliorer le support x86 dans leurs différentes applications, bibliothèques, frameworks…

Mon but au travers cet article - et sans doute de prochains, plus spécifiques - est de partager mon expérience dans ce domaine, qui pourra certainement vous aider si vous projetez d'améliorer la compatibilité de votre application.

N'hésitez pas à commenter cet article ! Commentez

- Titre : Comment améliorer le support X86 des applications et bibliothèques sur Android

- Auteur : Xavier HALLADE

- Parution : 16 décembre 2013

- Licence : Copyright ® 2013 Intel. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Pourquoi faire quoi que ce soit pour le support de versions x86 d'Android ?

- I-A. Il y a des produits Android sur le marché, tournant sur des architectures x86

- I-B. Et ces produits sont compatibles avec l'écosystème d'applications actuel

- I-C. Mais cela reste vraiment important de fournir une version x86 de vos bibliothèques natives

- II. Comment compiler/obtenir une version x86 de vos bibliothèques natives

- II-A. Vérifiez si vous utilisez des bibliothèques natives

- II-B. Si vous utilisez la chaîne de compilation du NDK Android

- III. Si ces fichiers .so proviennent de bibliothèques ou frameworks dont vous n'avez pas la source

- IV. Problèmes courants

- IV-A. Compilation

- IV-B. Exécution

- IV-C. Optimisation

- IV-D. Réduire le poids de votre application

- V. Liens Utiles

- VI. Remerciements Developpez

I - Pourquoi faire quoi que ce soit pour le support de versions x86 d'Android ?

Android sur des plates-formes x86 est loin d'être quelque chose de totalement nouveau.

Le projet android-x86.org par exemple existe depuis 2009, la première Google TV (2010) était à architecture Intel… et surtout depuis 2011, la chaîne de compilation x86 fait officiellement partie du Android NDK (Native Development Kit).

Intel a beaucoup contribué à améliorer le support du x86 directement dans l'AOSP (Android Open Source Project), et est aussi derrière le projet android-ia.org (2012) qui propose une distribution complète Android qui peut être installée sur des PC avec UEFI.

Vous pouvez aussi faire tourner des versions x86 d'Android sur des PC à travers l'émulateur Android du SDK (accéléré par notre HAXM ou kvm) ou d'autres produits comme Genymotion - qui représente plus de 500 000 utilisateurs actuellement !

I-A - Il y a des produits Android sur le marché, tournant sur des architectures x86

Depuis 2012, il y a plus encore que ces différents projets : vous pouvez trouver des smartphones et des tablettes x86 avec Android en vente libre. Le premier en France était le « Orange avec Intel Inside », et voici quelques autres exemples un peu plus actuels : Samsung Galaxy Tab 3 10.1′, Asus MemoPad FHD 10, Motorola Razr I… et bientôt l'Asus Fonepad Note 6 (déjà disponible chez nos voisins).

I-B - Et ces produits sont compatibles avec l'écosystème d'applications actuel

Les applications Android sont gérées par la machine virtuelle Dalvik (DVM), ou le projet émergent Android RunTime (ART). Ces technologies sont multiarchitectures par nature et marchent très bien sur x86 !

Néanmoins certaines applications (entre 40 % et 60 %) utilisent aussi des bibliothèques spécifiques au CPU… et dans de nombreux cas pour le moment, ces bibliothèques ne sont incluses que pour les architectures ARM.

Par « chance », les produits à architecture Intel qui sont dans le commerce embarquent tous un élément particulier appelé « NDK apps bridging technology » qui permet à ces binaires ARM de fonctionner à travers une couche de compatibilité. C'est pourquoi dans la pratique, vous pourriez vous-même avoir des difficultés à deviner si un produit est x86 ou non.

I-C - Mais cela reste vraiment important de fournir une version x86 de vos bibliothèques natives

•« NDK Apps Bridging Technology » est la propriété d'Intel, vous ne la retrouverez pas forcément dans des produits tiers ;

•même si cette technologie marche très bien, une telle couche de compatibilité entraîne forcément une baisse de la performance et une hausse de la consommation par votre application - qui ne sont pas négligeables si vous faites des opérations un peu lourdes ;

•vous ne la contrôlez pas : les anciens modèles de smartphones embarquent une ancienne version de la technologie dont vous ne pouvez pas espérer un aussi bon fonctionnement que dans le cas des versions actuelles ;

•votre application pourra forcément mieux tourner si des versions x86 de vos bibliothèques sont incluses, et habituellement ce n'est pas une grosse charge de les compiler/obtenir et les intégrer.

II - Comment compiler/obtenir une version x86 de vos bibliothèques natives

II-A - Vérifiez si vous utilisez des bibliothèques natives

La première chose à faire est de regarder si vous embarquez des bibliothèques natives dans votre application.

Bien sûr, cela n'arrive pas par hasard ! Si vous utilisez vous-même le NDK Android, vous savez déjà que votre application embarque des bibliothèques natives… Peut-être même que tout le code de votre application est écrit en C/C++ (ex. : vous utilisez native_app_glue.h).

Mais ces fichiers .so (shared object libraries) peuvent aussi être générés par des moteurs de jeu/frameworks ou inclus par des bibliothèques que vous utilisez.

L'emplacement habituel pour ces fichiers est dans libs/TARGET_ARCH_ABI où TARGET_ARCH_ABI peut être, à l'heure actuelle : armeabi (ARMv5), armeabi-v7a (ARMv7), mips ou x86.

II-B - Si vous utilisez la chaîne de compilation du NDK Android

Commencez par configurer APP_ABI:=all dans votre Makefile « jni/Application.mk » (et non Android.mk! vous aurez peut-être à le créer), pour changer la valeur par défaut qui est « armeabi ».

La prochaine fois que vous compilerez votre code, les bibliothèques seront générées pour toutes les architectures que le NDK supporte, dans libs/TARGET_ARCH_ABI/*.so :

Simple, non ?

Habituellement cela se passe très bien…mais utiliser une nouvelle chaîne de compilation peut mettre en lumière certains bogues ou erreurs latentes dans votre code. Un exemple simple est lorsque vous avez du code qui ne respecte pas le standard C/C++ : le résultat peut changer selon l'implémentation faite dans le compilateur.

III - Si ces fichiers .so proviennent de bibliothèques ou frameworks dont vous n'avez pas la source

Commencez par mettre à jour ces éléments vers leur dernière version, le support du x86 a pu leur être ajouté récemment.

De nombreuses bibliothèques et moteurs supportent x86 directement, voici une brève liste des plus utilisés pour lesquels x86 est officiellement supporté : libgdx, cocos2dx, fmod, AppGameKit, Unreal Engine 3, Havok Anarchy SDK, Marmalade, lame, OpenCV, etc.

IV - Problèmes courants

IV-A - Compilation

La chose la plus évidente qui peut stopper la compilation x86 est d'avoir du code assembleur ARM quelque part… vous obtiendrez alors de telles erreurs :

Error: no such instruction: `ldr r12,[r0]'
Error: no such instruction: `smull %edx,%esi,%eax,%ecx'
Error: number of operands mismatch for `mov'
Error: impossible constraint in 'asm'

Habituellement, cela arrive avec des bibliothèques multiplateformes qui ont leurs variables de compilation configurées incorrectement pour Android - entraînant l'inclusion de code spécifique ARM - et vous avez seulement besoin de les corriger(1)

C'est le cas le plus courant que j'ai rencontré, je traiterai ce cas plus spécifiquement dans de prochains articles.

IV-B - Exécution

Les techniques classiques de débogage (gdb…) marchent toujours lorsque vous utilisez l'image x86 d'Android à travers l'émulateur du SDK (+HAXM/kvm) ou Genymotion.

C'est aussi une bonne idée d'utiliser ces outils régulièrement puisqu'ils sont bien plus rapides que l'émulateur du SDK Android dans sa configuration par défaut.

IV-C - Optimisation

-O3 et -ffast-math sont les CFLAGS classiques que vous pouvez ajouter pour permettre un maximum d'optimisations par le compilateur lorsque vous visez n'importe quelle plate-forme.

Vous pouvez aussi spécifier des optimisations supplémentaires spécifiques aux plates-formes que vous visez. Dans le cadre d'une compilation x86, il est conseillé de spécifier ceci dans votre Makefile jni/Android.mk :

ifeq ($(TARGET_ARCH_ABI),x86)
LOCAL_CFLAGS += -mtune=atom -mssse3 -mfpmath=sse
endif

Le jeu d'instructions SSSE3 est disponible sur tous les systèmes Android à base x86 donc vous pouvez l'utiliser directement. Vous pourrez aussi avoir une bonne hausse de performances (cela peut être aux alentours de 20 %), en passant du jeu d'instructions x87 à SSE pour les opérations en virgule flottante.

IV-D - Réduire le poids de votre application

Nul besoin de sacrifier un dossier de bibliothèques natives si vous cherchez à réduire le poids de votre application. Vous pouvez scinder votre .apk pour en avoir une version par architecture tout en gardant une seule entrée dans le Google Play Store.

Commencez par passer du mode simple au mode avancé dans votre console développeur. Puis envoyez différents APK faits pour différentes architectures, avec pour chacun une valeur android:versionCode différente, configurées dans le fichier AndroidManifest.xml.

Si vous voulez que le bon APK aille sur le bon système, il n'y a qu'une seule règle à suivre :

Numéro de version x86 > Numéro de version ARMv7 > Numéro de version ARMv5

Car un unique système peut être compatible avec l'ensemble de ces architectures, auquel cas le Google Play Store servira l'APK compatible dont le nombre « android:versionCode » est le plus élevé.

Vous pouvez appliquer cette règle de la manière que vous le souhaitez, mais il est plus simple de le faire en suivant une convention telle que la suivante : préfixez votre numéro de version actuelle par un chiffre représentant l'architecture supportée.

V - Liens Utiles

L'original de cet article en version anglaise est disponible directement sur mon blog : How to improve x86 support of android apps, libs, engines.

Voici quelques liens supplémentaires qui pourront vous être utiles :

•NEON to SSE3 intrinsics wrapper ;

•Understanding ARM vs x86 memory alignment on Android ;

•Configuring Virtual Machine Acceleration for Android SDK emulator.

VI - Remerciements Developpez

L'équipe Android de Developpez.com tient à remercier Intel Developer Zone et Xavier Hallade pour la rédaction de ce tutoriel.

Retrouvez le Blog de Xavier Hallade, Software Engineer chez Intel.

Pour toute question, merci de contacter Slim Soussi, EMEA Program Manager chez Intel : slim.soussi@intel.com

Nos remerciements à Zoom61 pour sa gabarisation.

N'hésitez pas à commenter cet article ! Commentez

(1)Si vous avez réellement uniquement de l'assembleur ARM dans votre projet, vous aurez évidemment besoin de développer un équivalent x86 ou en C/C++ - mais c'est rare.