08

15 jours de dev sur l’Intel z2460.

juil

Cela m’aura pris 2 semaines, mais j’ai fini par porter mon moteur 2D sur l’Intel z2460!
Retour d’expérience assez intéressant! Car développer sur Android c’est déjà sport, mais alors coder en natif pour le x86, ça devient carrément le grand saut dans l’inconnu ! Rien n’est réellement fini, ni complètement fonctionnel!
Du coup on peut passer un temps assez incroyable à chercher les raisons d’un tout petit problème !!! Bon au final, ça vaut quand même le coup ! L’Intel étant comme je le pressentais plutôt assez performant !

Développer pour Android x86

J’avais (il y a 15 jours de cela) été plutôt impressionné par la simplicité de la recompilation d’un code pour sur la plateforme x86! Il suffisait alors d’ajouter simplement la plateforme x86 à la variable APP_ABI contenu dans le fichier Application.mk

	APP_ABI := armeabi armeabi-v7a x86

Le code C était alors compilé en plusieurs versions et assemblé dans un module unique. Android se chargeait alors d’utiliser la bonne version du code en fonction du device utilisé ! c’était simple et c’était beau…

C’était même trop beau en fait !!!!

Car si cela avait fonctionné à merveille pour mon test en C (la chance du débutant sans doute), l’utilisation du C++ allait s’avérer nettement plus pénible !

L’autre problème qui est rapidement devenu lourd c’est l’impossibilité d’exécuter un programme directement sur le téléphone Orange depuis éclipse.
Intel fourni bien un driver, mais le téléphone reste invisible.
Si bien que pour tester, il m’a fallu déposer, à chaque nouvelle compilation, l’apk sur mon serveur pour pouvoir ensuite le télécharger sur le téléphone et l’installer. Super simple, hyper efficace, on n’a pas du tout l’impression de perdre son temps !

Enfin et pour couronner le tout, certaines lib c++ ne fonctionnent tout simplement pas correctement !

Retour sur le développement C++

Dès que j’ai voulu créer un objet C++ je me suis heurté au plantage direct de mon application.
Aucun doute possible, l’opérateur new fait crasher mes programmes (tout du moins avec les options de compilation que j’ai mises).
Je ne vais pas trop expliquer les “pourquoi” et les “comment”, car je ne suis pas assez balaize en C pour avoir compris la raison du fonctionnement (ou non) de mes différents essais. J’ai cherché sur Internet et j’ai essayer un peu tout ce que j’ai pu trouver !

Je liste ci-dessous (dans l’ordre) les problèmes que j’ai rencontré et comment je les ai résolus !

Problème: Crash de l’opérateur new
Solution: Ajouter dans le fichier Application.mk

	APP_STL := gnustl_static

Problème: Crash de la fonction memset
Solution: Re coder en C (ou assembleur) cette fonction.

Problème: Crash de toutes les fonctions nécessitant l’inclusion dans la librairie <string .h>. (Si j’ai bien compris, c’est un problème de définition de size_t)
Solution: N’utiliser aucune de ces fonctions !

Problème: Crashs intempestifs (et pour autant régulier, c’est à dire toujours au même moment, mais ne pouvant pas débugger…)
Solution: Mettre à jour le ndk et passer à la version 8 !

Voilà. Il est possible que le passage au ndk r8 ai solutionné des problèmes survenu en amont, mais j’ai pas eu trop le courage de refaire les essais !!!

Au final, ça fonctionne, même s’il me reste un petit bug à résoudre.
Sur l’Intel: La détection de multitouch semble parfois détecter mes doigts au mauvais endroits ! Il est possible que ce soit un bug dans mon code !

Remarque : plus d’une semaine quand même pour réussir à résoudre tous ces problèmes !

Portage de mon moteur pour l’Intel Z2460

Une fois mes petits problèmes d’exécution réglés (car évidement au niveau de la compilation il n’y avait aucun problèmes), j’ai dans un premier temps porté tout mon moteur en C.
Cette opération m’a permis d’avoir une vue intéressante sur l’optimisation moyenne de l’assembleur. Lorsqu’on optimise une fonction ont peut atteindre des performance 10 fois supérieures en Assembleur par rapport au C, mais en fait, un programme complet, lui, n’est jamais entièrement codé en assembleur, et l’optimisation globale est souvent bien plus faible !

Le portage en C c’est pas été trop compliqué ! C’est byzarre, mais il y avait pas mal de fonctions que j’avais codées directement en assembleur, il a donc fallu que je recode l’équivalent en C.
Une fois tout cela réalisé, j’ai pu enfin comparer deux choses :

  • Intel vs ARM
  • C vs Assembleur

Et voilà les résultats !

Arm C++ vs Intel C++

Comparons déjà les versions C++

Décompression Traitement Tracé Blit Screen
Galaxy Note 1280*800 3.31 s 7.94 s 17.65 ms 3.71 ms
Galaxy Note 1024*600 3.33 s 6.65 s 8.58 ms 2.47 ms
Intel Inside 1024*600 3.27 s 6.20 s 6.86 ms 1.38 ms

Avant d’analyser les résultats voici quelques explications sur les différentes parties benchées.

Décompression : J’utilise la compression LZMA (qui soit dit en passant est ce qui se fait de mieux actuellement) pour réduire la taille de mon exécutable. Ce code est donc le code de décompression (mono coeur) du LZMA.

Traitement : Une fois les données décompressées, je converti tous les sprites en les réduisant (ou les agrandissant) grâce aux fonctions bilinéaires expliquées ici afin qu’ils soit adaptés à la résolution du device. Enfin je les réduis en 16 bits puis les encode dans un format particulier requis par mon moteur !

Tracè : La procédure de tracé contient l’ensemble des entrées/sorties du moteur. En gros, détection des doigts, modification de la scène et tracé des sprites.

Blit Screen : Comme expliqué ici et , on ne peut pas tracer directement sur l’écran en code natif. On trace donc dans une bitmap (de la taille de l’écran) que l’on blit ensuite en JAVA ! Le temps indiqué ici est donc le temps pris pas le Blit Java.

Pour que les résultats puisse être comparés j’ai testé mon moteur sur la Galaxy Note avec la même résolution que celle du téléphone d’Orange, à savoir 1024 * 600 pixels.

Alors ça dit quoi ?

On note tout de suite que quelque soit la partie du code concernée, l’Intel (à résolution égale) est toujours plus performant que le Galaxy Note !

La décompression LZMA réalise des accès mémoire très aléatoires ! Les deux processeurs sont donc pénalisés de la même manière !

Le traitement des pixels est un peu à l’avantage de l’Intel, mais rien de spectaculaire. L’ARM est sans doute un peu plus rapide pour les calculs, et l’Intel meilleur dans la gestion des caches !

L’écart se creuse en faveur de l’Intel avec le tracé des sprites en C. L’accès à la mémoire est plus séquentiel, et la gestion du cache de l’Intel doit faire la différence.

Enfin le Blit Java est sans appel ! Evidement il y a plein de chose que l’on ignore ici !

  • NEON est il utilisé par le Java pour le blit ?
  • SSE est il utilisé par le Java pour le blit ?
  • Mais surtout le bitmap fournie est 16 bits ! Y a t-il besoin d’une conversion de pixels (car pour le moment j’ai pas été capable de trouver le nombre de bits des écran des devices Android. Il semble que cette information soit indisponible).

On ne peut donc que constater le résultat !

Bon enfin tout cela n’enlève en rien aux mérites du z2460. Pour le moment, on peut dire qu’il tient la route face à l’ARM 9 du Galaxy Note !

C++ vs Assembleur

L’autre partie intéressante de ces tests est la comparaison entre le Code “full C++” et l’assembleur.
Ici point d’Intel car je n’ai pas encore re codé en assembleur Intel les partie critiques.

Décompression Traitement Tracé Blit Screen
GNote C++ 1280*800 3.31 s 7.94 s 17.65 ms 3.71 ms
GNote Asm 3.31 s 4.56 s 6.58 ms 3.57 ms
Ratio 1.71 2.68
GNote C++ 1024*600 3.33 s 6.65 s 8.58 ms 2.47 ms
GNote Asm 3.37 s 1.56 s 4.13 ms 2.46 ms
Ratio 4.26 2.07

On ne s’intéresse pas ici à la décompression et au Blit Java, puisque le premier est entièrement en C et le second, entièrement en Java !

Le traitement des sprites (redimensionnement, conversion, …) semble fortement impacté par la résolution du device. Cela peut sembler possible puisque les “cache miss” doivent être plus nombreux au fur et à mesure que la résolution des images traitées augmente.

C’est plus étonnant pour la routine de tracé qui semble, elle, avoir une évolution inverse !!! Difficile à interpréter.
Il est vrai que j’avais déjà modifié cette fonction afin qu’elle réduise les accès mémoire aléatoires lorsque j’avais eu mon Galaxy Note.

Pour la petite anecdote : A l’époque, le Galaxy Note m’avait semblé très lent comparé au Galaxy Tab. La raison venait du parcourt d’une liste chainée dont la taille grandissait 2 fois plus vite que le nombre de pixels affichés. Du coup les éléments de la liste ne tenaient plus dans le cache. Un simple ré ordonnancement au début de chaque frame avait solutionné le problème.

Conclusion

Bon je me suis un peu égaré dans se post sur la fin, mais ce n’est pas bien grave !!!

Pour en revenir au z2460 d’Intel !
Plus de doute possible, il s’agit d’un processeur très efficace qui se compare sans aucun problème aux Cortex A9 en terme de performances.
Par contre le développement du code natif pour l’architecture x86 est encore laborieux !
Ca devrait vite s’améliorer je suppose. Cela laisse quelques semaines de plus à ARM pour regarder d’un oeil plutôt inquiet ce nouveau concurrent !

Il me reste à porter le Code C en assembleur Intel pour voir jusqu’où on peut pousser la bête !!!
Je mettrai ce post à jour une fois que ce sera fait !

 | Tags:

10 Responses to “15 jours de dev sur l’Intel z2460.”

  1. 0xFF dit :

    Donc on va bientot devoir avoir creer deux versions assembleur pour ARM + Intel ? pas une bonne nouvelle !

  2. Etienne SOBOLE dit :

    Bah c’est sur que “normalement” on ne met pas de l’assembleur partout !!!
    Ceci dit, tu avais déjà besoin de deux codes distincts (NEON et pas NEON pour le tegra 2)
    Maintenant il faut ajouter x86 et MIPS :)

  3. Plokta dit :

    La comparaison C vs Asm du compilo x86 devrait être intéressante, GCC en X86 n’a pas une aussi mauvaise réputation que la version ARM.
    L’utilisation des intrisics devrait aussi être moins catastrophique sur X86 que sur ARM ce qui amènerait à des portages Intel plus facile ( j’ai personnellement complètement arrêté l’asm au profit des intrisics sur windows ).
    Dans tous les cas, la mise à jour du NDK avec un GCC 4.7 devrait remettre en cause tous les tests.

  4. Etienne SOBOLE dit :

    Ouai le gcc 4.7 va être beaucoup plus performant si on se base sur les présentations de linaro !
    Par contre j’avoue ne pas très bien connaitre la programmation intrinsic ! Est-ce que c’est compatible entre tous les processeurs ?

  5. Plokta dit :

    Non, l’intrisic n’est pas compatible avec les différents cpu, mais permet d’utiliser neon ou sse sans passer par l’assembleur.
    par exemple:

    static __inline uint8×8_t
    ComponentMult( uint8×8_t iA, uint8×8_t iB )
    {
    uint16×8_t i = vmlal_u8( vdupq_n_u16( 128 ), iA, iB );
    return vmovn_u16( vshrq_n_u16( vsraq_n_u16( i, i, 8 ), 8 ) );

    //uint16 i = iA * iB + 128;
    //return (i + (i>>8) ) >> 8;
    }

  6. Plokta dit :

    version SSE2:

    static __inline __m128i ComponentMult( __m128i iA, __m128i iB)
    {
    const __m128i cst128 = _mm_set1_epi16(128);
    __m128i i = _mm_adds_epu16( _mm_mullo_epi16( iB, iA), cst128);
    return _mm_srli_epi16( _mm_adds_epu16(i , _mm_srli_epi16(i, 8)), 8);
    }

  7. Etienne SOBOLE dit :

    Oui je connais le principe, je me demandais juste si ça offrait la portabilité !
    en fait les instructions que tu utilises sont très proche des instructions assembleur !!! La seule différence c’est que tu laisses au compilateur la tâche de la gestion des registres.

    En tout cas je suis content de voir que tu as l’air de savoir utiliser sse !!! Les codeurs SSE n’ont pas l’air d’être beaucoup plus nombreux que les codeurs NEON !

    Si t’es ok je te demanderai peut être quelques conseils !

  8. Plokta dit :

    En fait, je subis plus le SSE que je sais l’utiliser, j’ai l’impression d’avoir besoin de relire toute la doc à chaque fois que je m’y remets. NEON, qui est relativement neuf pour moi, m’a rapidement plus, mais l’approche est vraiment différente, il n’y a pas de fonction de dés-entrelacement dans SSE ( genre vld4_u8() ).

  9. Plokta dit :

    Le nouvel NDK r8b est sorti, avec la mise à jour 4.6 du GCC. Mes premiers tests donnent entre 5 et 15% de perf en plus sur le code ARM sans NEON.
    Je testerai le code intrinsic plus tard, mais je pense que ça va être bien mieux.

  10. Etienne SOBOLE dit :

    Bon timing ! Cette mise à jour arrive au bon moment ! C’est excellent. Je vais aller tester aussi, même si la partie C de mes codes est plutôt contenue ;)

Répondre

Human control : 6 + 7 =