Accueil > Mobile, Outillage > Android : optimisation des performances 3 – Le code natif

Android : optimisation des performances 3 – Le code natif

Après avoir passé en revue les problématiques de performance relatives à la fluidité de l’interface et à l’utilisation de la mémoire, je vais maintenant vous présenter deux alternatives permettant de faire du natif. L’utilisation du langage Java peut parfois être une barrière quand il s’agit de faire des algorithmes nécessitants de grosses performances, tels que du traitement vidéo, de la simulation, ou toute autre opération utilisant intensivement le processeur. Afin de se rapprocher de la machine, Android fournit deux méthodes pour écrire du code bas-niveau performant :

  • le NDK, qui permet de faire du code natif C/C++
  • et RenderScript, solution propre à Android basée sur la syntaxe C99

Le fait qu’il existe deux façons de faire natif peut être déroutant, mais les approches sont assez différentes et chacune apporte son lot d’avantages et d’inconvénients que je vais tenter de décrire.

NDK

Le NDK est un ensemble d’outils permettant d’embarquer dans une application Android des composants logiciels écrits en C ou C++. Il fournit un ensemble de librairies standards, ce qui peut permettre de réutiliser du code C/C++ existant pour l’intégrer à une application Android. Le code est compilé, et ne peut fonctionner qu’avec l’architecture pour laquelle il a été compilé (ARM v5, ARM v7, x86), ce qui impose de générer un APK pour chaque architecture cible.

L’interface entre la couche Java et la couche native est faite avec JNI (Java Native Interface), ce qui est fait couramment dans le monde Java.

En résumé, les plus :

  • C/C++ standard, réutilisable
Les moins :
  • JNI lourd à mettre en place
  • Un APK par type de processeur

RenderScript

Alors que le NDK existe depuis Android 1.5, RenderScript (RS) est apparu avec Honeycomb (Android 3.0). Il permet d’écrire du code bas niveau, mais contrairement au NDK, il est indépendant d’une plate-forme. Il peut exécuter le code sur différents types de processeurs : actuellement uniquement les CPUs sont supportés (ARM, x86), mais un support GPU et DSP est prévu. Afin de permettre ce fonctionnement multi-architecture, le code est compilé directement par l’appareil au runtime. RenderScript permet aussi de paralléliser le code sur plusieurs coeurs, de manière automatique et intelligente. A cause de cette particularité multi plate-forme, les librairies standards du NDK ne sont pas disponibles sous RenderScript. Toutefois, d’autres APIs ciblées pour les calculs à haute performance sont fournies : mathématiques, gestion de la mémoire, matrices, vecteurs, etc. Il est notamment très adapté pour faire du traitement d’images car il permet de faire des choses complexes en très peu de lignes.

Afin d’illustrer la syntaxe et les possibilités de RenderScript, voici un extrait de code que l’on peut trouver sur les exemples officiels et qui est parfaitement expliqué sur ce blog. Il s’agit d’un algorithme de saturation d’image :

void root(const uchar4 *in, uchar4 *out, uint32_t x, uint32_t y) {
    float3 pixel = convert_float4(in[0]).rgb;
    pixel = rsMatrixMultiply(&colorMat, pixel);
    pixel = clamp(pixel, 0.f, 255.f);
    pixel = (pixel - inBlack) * overInWMinInB;
    if (gamma != 1.0f)
        pixel = pow(pixel, (float3)gamma);
    pixel = pixel * outWMinOutB + outBlack;
    pixel = clamp(pixel, 0.f, 255.f);
    out->xyz = convert_uchar3(pixel);
}

On voit que grâce aux types et fonctions fournis dans RenderScript, il faut très peu de lignes de code pour faire cet algorithme. Il en aurait fallu quatre fois plus en C/NDK.

En pratique, Google a utilisé RenderScript pour le rendu du mur 3D de son application YouTube, ainsi que pour l’application Music.

L’autre particularité est que les fonction écrites en RenderScript n’ont pas de valeur de retour, elles sont asynchrones : lorsqu’on fait appel à une fonction RS depuis Java, l’appel est placé dans une file d’attente, est exécuté dès que possible. Si besoin, le code RS peut ensuite envoyer un message à la couche Java lorsqu’il a terminé un calcul, afin de lui fournir un résultat.

Les plus :

  • Multi plate-forme
  • Potentiellement plus performant
Les moins :
  • Framework propre à Android qui demande une période d’apprentissage

Conclusion

Dans les deux cas, ces frameworks ne sont pas là pour remplacer Java : ils ne permettent pas de construire des applications de A à Z avec du code natif, car Android reste un framework Java. Comme cela augmente la complexité du logiciel, on ne les utilise que lorsque l’on doit écrire des fonctions indépendantes qui ont de gros besoins en termes de performance. S’il faut les comparer, je pense que RenderScript est plus simple à mettre en place, à utiliser, il est potentiellement plus performant, et devrait donc être la solution privilégiée pour faire du natif, tandis que le NDK tend à devenir moins utile, sauf dans le cas où on voudrait réutiliser du code C/C++ existant.

Pour ce qui est des graphismes 3D, il ne faut pas foncer tête baissée vers une solution native : l’API OpenGL de Java est très performante car les calculs sont faits par le GPU.

Bon code !

Mise à jour 14/11/2012 : L’équipe Android a annoncé que la tablette Nexus 10 sous Android 4.2 sera le premier appareil à pouvoir exécuter le Renderscript directement sur le GPU (source).

Categories: Mobile, Outillage Tags: , ,
  1. Pas encore de commentaire
  1. Pas encore de trackbacks


− sept = 2