vendredi 10 janvier 2014

Air, Workers et Big data…

Flash et son langage (l'ActionScript, alias AS3) ont la réputation d'être lent, ce qui est relatif mais globalement vrai. Il peut donc paraître inadéquate ­ voire illusoire ­ de vouloir utiliser la technologie d'Adobe pour traiter de (très) gros volumes de données à des fins statistiques… c'est pourtant ce que j'ai fait pour un client… 



L'ActionScript n'est pas un langage compilé (même si Adobe parle volontiers de "compilation" dans sa documentation), ni un langage interprété mais un langage à "Bytecode" (c'est-à-dire à code intermédiaire) qui permet de faire tourner le même .swf (fichier généré) sur différentes plateformes (pc Windows, mac, linux, certains Android, …) avec des performances très correctes. C'est le même principe que Java à l'origine (et la JVM/Java Virtual Machine). Même lorsqu'on génère un .exe (format exécutable de Windows), on crée en réalité un fichier contenant à la fois le lecteur (machine virtuelle) et le programme (le .swf). Un langage à Bytecode sera par principe (et cela se vérifie quasi systématiquement dans la pratique) un langage plus lent qu'un langage compilé. Il répond à d'autres besoins et à une autre stratégie. Il est donc ­ en théorie ­ à réserver aux applications pour lequel il a été destiné (les applications/animations/jeux dans le navigateur Internet pour Flash/AS3). Air (qui est la version "desktop" de Flash) utilise sensiblement la même machine virtuelle et n'apporte donc pas de gain de performances. 

Il n'était donc pas évident de vouloir utiliser Air/AS3 pour réaliser un logiciel d'analyse statistique sur des fichiers Excel de taille conséquente… Mais forcément ce genre de défit est ce qui me motive (comme lorsque j'ai réalisé un raytracer ­ simpliste ­ en temps réel avec la même technologie ->http://www.henriblum.com/article8/raytracing-temps-reel-en-flash-partie-4)… L'objectif ici était de parcourir des fichiers Excel (en .csv) de plusieurs (dizaines de) millions de lignes, contenant un texte (dont je ne parlerai pas ici du contenu, pour respecter le souhait de confidentialité de mon client). Chaque ligne contient un court texte et un numéro de "catégorie". Ces fichiers font plusieurs centaines de Mo (voire Go)… Mon Excel n'arrive même pas à les ouvrir (et les coupe au-delà d'un certain nombre de lignes). D'ailleurs aucun des logiciels présent sur ma machine n'étaient capable de les ouvrir ! 
Le but de mon client est de chercher dans chaque texte de chaque ligne toutes les combinaisons de chaînes de caractères (de toutes les longueurs possibles) puis de rechercher ces chaînes dans tout le reste du fichier. L'idée étant de connaître pour chaque "combinaison" le pourcentage de présence dans chaque catégorie (qui est une des colonnes des lignes du fichier). Il s'agit donc de boucles dans des boucles dans des boucles dans des boucles…  

premier algorithme sur ticket de caisse Starbucks / first algorithm on receipt Starbucks
Je réalise un premier test sur des fichiers (artificiels) de différentes tailles pour essayer d'extrapoler les temps sur des fichiers "réels" (donc de taille importante). Les temps "déduis" font tourner la tête, en laissant entrevoir plusieurs MOIS continus de calculs pour traiter un fichier de plusieurs dizaines de millions de lignes ! Autant dire qu'à priori c'est mal parti… Mais je savais qu'il existait de multiples pistes d'optimisation, donc je rassure le client (très sympa et très confiant) et je continue mes expérimentations…

La première piste est par exemple de limiter les chaînes de caractères à traiter. Par exemple par leur longueur. En effet les chaînes de 2 (ou moins) caractères sont très probablement des articles (le, la, un, à, …) sans intérêt sémantique (pour mon client). Cela permet d'éliminer un nombre très important de chaînes à traiter (dans les 20% environ). On retombe à plusieurs SEMAINES de calcul... Le temps de traitement étant exponentiel, toute optimisation à de bonnes conséquences sur le temps de traitement.

Je limite ensuite les recherches en stockant les recherches précédentes déjà réalisées. En effet si "toto" a déjà été recherché dans tout le fichier car présent dans la ligne 1, il n'est pas utile de le rechercher encore dans tout le fichier si il est de nouveau présent dans la ligne 2. Cela consomme de la mémoire (pour stocker les millions d'expressions déjà traitées) mais la piste est bonne. Le fichier contenant des textes "particuliers" (liés à l'activité de mon client) dont la récurrence est importante, c'est encore une optimisation très efficace… On descend à plusieurs JOURS de calcul...

Je continue ensuite en commençant les recherches par les chaînes les plus longues. En effet si "tototo" a déjà été traité, les statistiques sur "toto" (qui est contenu dans la chaîne "tototo") s'en trouvent simplifiées. Cela oblige un pré­traitement (découper les chaînes et les trier par taille) mais qui est finalement payant en terme de performance globale. On descend à plusieurs HEURES de calcul...

Après de nombreux essais et une collaboration productive avec le client, je parviens à diverses autres optimisations et je pense avoir fait le tour de ces "trucs". Il faut maintenant passer à la vitesse supérieure…

Les Workers 
Adobe a introduit avec la version 11.4 de sa machine virtuelle (Flash Player) la gestion du multithread avec les "workers". Jusqu'à présent le programme écrit par le développeur et le "player" s'exécutaient dans le même "thread" (processus) ce qui aboutissait à un essoufflement rapide de votre programme. Désormais Flash peut ­ à partir du thread "principal" ­ lancer d'autres "threads". Il est en réalité très exagéré de parler de parallélismes ou d'utilisation de "cœurs" de processeur, mais l'idée est intéressante et permet d'aborder de "gros" traitement plus sereinement.

  L'utilisation de ces "workers" est vraiment simple (voir la description de la classe ici :http://help.adobe.com/fr_FR/FlashPlatform/reference/actionscript/3/flash/system/Worker.html) . Il existe pas mal d'exemples, même si je m'aperçois vite que ces codes n'abordent que rarement des cas réels et le problème que le "parallélisme" entraîne : la communication entre les différents Threads !
En effet, Adobe met à disposition plusieurs techniques pour faire communiquer les codes sur des threads différents, mais ces techniques sont (relativement) lentes ! Les processus (indépendants) peuvent par exemple utiliser un "canal" de communication (MessageChanel), ou partager un espace mémoire commun (ByteArray), … mais au final tout cela n'est pas très rapide (sans parler des problèmes d'accès simultanés à la même mémoire…). On se retrouve vite avec des processus ­ relativement ­ rapides mais qui perdent énormément de temps en s'échangeant des données et en attendant des réponses… Un certain gâchis. Mais un problème classique en traitement parallèle (même si je ne suis pas un spécialiste).

Un bon site explique le problème : ActionScript Worker Message Passing is Slow

Toutefois je ne peux me passer de ces workers. En effet non seulement ils peuvent accélérer le traitement (en parallélisant les recherches) mais surtout évite de surcharger le thread principal qui est aussi chargé de l'affichage de l'interface utilisateur et de divers tableaux de bord pour suivre le traitement. Idéalement Adobe, qui conseille de limiter le nombre de workers, recommande dans ce genre de cas de créer un worker pour le traitement (les calculs) et un autre (le principal) pour l'affichage. C'est effectivement une organisation logique mais qui même ainsi doit être creusée. En effet il faut limiter au maximum les échanges entre les deux pour éviter les temps d'attente décris précédemment. Je choisis donc de limiter les échanges, ce qui entraîne un affichage moins "fluide" de mon tableau de bord (qui loupe des choses) qui n'est de toutes façons qu'un "plus". Ainsi je gagne encore pas de temps plutôt que d'échanger de façon exhaustive les infos. Un parti pris qui ne nuit pas au résultat final ni réellement au confort d'utilisation du logiciel.


Evidemment l'étape suivante fut de passer à l'utilisation de plusieurs workers de traitement… Fortement déconseillé par Adobe (pour des raisons de stabilité j'imagine pour l'instant), mais tellement tentant… :­)
L'idée est donc de créer plusieurs processus, de leur donner à chacun une partie du fichier à traiter, de réunir le résultat de chaque traitement et de dédoublonner les résultats (si nécessaire). Il fut même envisagé de lancer cette application sur plusieurs machines en même temps puis de "mixer" les résultats. C'est ce qu'avait envisagé un temps le client (qui a accès à une grappe de calcul) mais à priori complexe à mettre en pratique pour lui.

Inutile de continuer ici à rentrer dans les détails techniques… Par contre, une conclusion s'impose… 


CONCLUSION
Ces "Workers" (qui désormais fonctionnent aussi sur les applications mobiles générées par Flash) permettent de compenser la relative lenteur de Flash/Air/AS3. Evidemment parler de Big Data dans cas est exagéré, mais le volume de données n'était pas anodin et leur traitement n'a été possible qu'au prix d'optimisations (le langage n'est pas tout). 
Si Adobe renforce la stabilité des "Workers" afin de pouvoir les utiliser en masse (si l'utilisation des processeur multi­cœurs est gérée) et leur communication, les applications Air/Flash pourraient être une bonne alternative pour du prototypage (ou du développement bon marché) pour le traitement de "grosses données"… si si !