Performance de lecture de plusieurs XML

muqaddarmuqaddar Administrateur
Salut,

J'ai une petite question performances.
Je le mets dans la rubrique commune même si c'est destiné à  l'iPhone.

Au lancement de l'application, je voudrais parcourir entre 20 et 50 dossiers de la sandbox qui possèdent chacun un fichier xml descriptif de leur contenu (qui porte le même nom dans chaque dossier). Chaque fichier XML a une trentaine de lignes.

Le but est de créer un array du contenu de tous ces fichiers xml et de l'exploiter dans l'application. Plus loin dans l'application, on pourra télécharger d'autres dossiers à  mettre dans la sandbox et donc "augmenter" un peu la taille de l'array de XML.

Une autre option était de créer une minibase SQLite ou bien même juste 1 seul fichier XML qui contient tous les autres au moment où on remplit la sandbox et/ou au moment où on télécharge un nouveau dossier.

Mais comme je suis fainéant, je voulais savoir si la première solution pouvait suffire (je pense que oui car parcourir un dossier qui en contient 30 autres et concaténer 30 fichiers XML dans un seul doit pas être trop gourmand).

J'ajoute que les XML ne sont pas des PLISTS.

Quelle stratégie adopteriez-vous ?

Réponses

  • AliGatorAliGator Membre, Modérateur
    décembre 2010 modifié #2
    Côté optimisation, moi je transformerai les XML en PLIST, c'est connu que le parsing des PLIST est optimisé (il y a eu un post à  ce sujet, citant un article qui comparait les différentes perfs des différents formats, il me semble, et aussi en fonction des différents moteurs XML utilisés, entre le NSXMLParser intégré ou les libs tierces)

    Ca dépend en fait de ce que tu attends comme perfs, et ce que tu considères comme acceptable.
    - Un PLIST ou une NSArchive me semblent plus optimaux à  parser qu'un XML
    - La concaténation de deux XMLs ne fait pas un XML valide (d'une part à  cause de l'en-tête XML, d'autre part parce qu'un XML valide n'a qu'une seule racine)
    - Mais bon après si ton parsing de chaque XML n'est pas trop long ça ne devrait pas être trop génant
    - A mon avis c'est un cas typique où tu peux considérer utiliser GCD. Rien qu'en parallélisant les analyses de tous les dossiers (suffit d'utiliser NSOperation ou même la méthode enumerateWithBlock de NSArray pour boucler sur ton tableau de dossiers, ça utilisera GCD implicitement sans que ça te perturbe plus que ça)

    NSArray* listOfFolders = ...<br />NSMutableDictionary* parserResults = [[NSMutableDictionary alloc] init];<br />[listOfFolders enumerateObjectsWithOptions:NSEnumerationConcurrent&nbsp; usingBlock:<br />^(id obj, NSUInteger idx, BOOL *stop) {<br />&nbsp; MyXMLParser* xmlParser = [MyXMLParser parserWithFile:obj];<br />&nbsp; MyXMLResult* r = [xmlParser parse];<br />&nbsp; [parserResults setObject:r forKey:obj]; // stocker le résultat dans un dico<br />}];<br />// ici dans parserResults tu as chaque résultat (objet hypothétique MyXMLResult)<br />// associé à  la clé correspondant à  chaque dossier<br />...<br />[parserResults release];
    
    Et hop, une utilisation implicite de GCD en faisant des boucles concurrentes (parallèles) :) Bon à  adapter à  ton cas avec ton parser perso et tout bien sûr
  • muqaddarmuqaddar Administrateur
    02:25 modifié #3
    Salut Ali,

    Pas mal la piste de GCD, sauf que je suis sur iPhone.  ;) (je sais je l'ai mis dans la section commune car je voulais en faire un sujet plus généraliste).

    Cependant, je partage ton analyse sur le PLIST et sa rapidité vis à  vis du XML à  parser à  chaque fois...

    Je pense donc remplir un PLIST général à  la volée avec les XML (au premier lancement de l'application pour ce qui est livré avec l'appli et à  chaque téléchargement pour le reste). Donc parser une seule fois le XML.
  • CéroceCéroce Membre, Modérateur
    02:25 modifié #4
    Je pense que ce qui va limiter la vitesse n'est pas le parsing, mais la lecture des fichiers. Or, lire un seul gros fichier est bien plus rapide que lire plein de petits fichiers (même avec de la mémoire Flash, elle aussi est découpée en secteurs). Question vitesse, utiliser une base SQLite binaire " qui n'accède aux données que lorsque c'est vraiment nécessaire " me semble donc une meilleure solution.


    Pour répondre à  ta question, "quelle solution adopteriez-vous", ma réponse est toujours la même: "la plus simple". Les utilisateurs préfèrent un logiciel imparfait demain qu'un logiciel parfait dans des mois. Réfléchis-y: garantir la synchro entre ces petits XML et un gros fichier central n'est pas si simple.
    Commence par lire les 50 XML et seulement si c'est trop lent, règle le problème.
  • AliGatorAliGator Membre, Modérateur
    02:25 modifié #5
    C'est quoi le problème avec GCD et l'iPhone ? Je vois pas le souci, GCD est supporté sur iOS aussi :P

    (Bon ok que depuis iOS 4.0, mais bon, je kiffe tellement les blocks et les possibilités qu'ils offrent :P)
  • muqaddarmuqaddar Administrateur
    02:25 modifié #6
    dans 1292928212:

    C'est quoi le problème avec GCD et l'iPhone ? Je vois pas le souci, GCD est supporté sur iOS aussi :P

    (Bon ok que depuis iOS 4.0, mais bon, je kiffe tellement les blocks et les possibilités qu'ils offrent :P)


    ça alors !
    Je l'avais passé à  la trappe ! 

    Mais bon, je voulais rendre l'application (pour un client) compatible avec le 3.2.
  • muqaddarmuqaddar Administrateur
    02:25 modifié #7
    @Céroce

    C'est vrai que je trouve le SQL plus souple qu'un PLIST et en plus j'ai l'habitude de travailler avec. J'ai l'impression d'avoir un peu tous les choix. :-)

    Réfléchis-y: garantir la synchro entre ces petits XML et un gros fichier central n'est pas si simple.


    C'est ce que je me disais, mais bon, j'y vois pas trop de contraintes non plus.
  • cyranocyrano Membre
    02:25 modifié #8
    C'est quoi le problème avec GCD et l'iPhone ? Je vois pas le souci, GCD est supporté sur iOS aussi


    juste pour info c'est les blocks qui sont supportés par iOS 4 ou GCD?

  • AliGatorAliGator Membre, Modérateur
    décembre 2010 modifié #9
    Les deux.
    Le principe de GCD repose sur des unités de travail qui sont automatiquement dispatchées par GCD sur les threads et les "dispatch_queues". Ces unités de travail sont représentées par des blocks.

    Autrement dit GCD utilise les blocks, donc si un OS supporte GCD, faut déjà  qu'il supporte les blocks (un OS pourrait supporter les blocks sans supporter GCD, mais aujourd'hui à  la fois OSX.6 et iOS4 supportent GCD et les blocks depuis la même version, ils sont apparus en même temps dans les dernières versions des OS d'Apple)

    ---

    Moi depuis qu'ils existent j'utilise pas mal les blocks y compris sous iOS, au début ça peut être un peu déroutant mais c'est vraiment puissant et simplifie grandement les choses en pratique. En particulier pour remplacer les callbacks. Par exemple pour demander une ressource réseau de façon asynchrone, plutôt que de réimplémenter à  chaque fois une classe avec NSMutableData et implémenter NSURLRequestDelegate et tout, on peut prévoir une classe qui s'utilise du genre :
    NSURLDownloader* downloader = [NSURLDownloader downloader];<br />[downloader downloadRequest:urlRequest completionHandler:^(NSData* receivedData) {<br />&nbsp; ... code exécuté quand la requête a réussi, receivedData contenant les données ...<br />} errorHandler:^(NSError* error) {<br />&nbsp; ... code exécuté quand la requête a échoué, error contenant le code d&#39;erreur ...<br />}];
    
    Ce que je trouve quand même plus sexy que d'avoir à  implémenter chaque méthode du protocole, donc dans des méthodes séparées qui ne sont du coup pas forcément à  côté du code qui lance la requête : au final avec les blocks tout est groupé et c'est plus lisible je trouve, pas vous ?
    En plus ça a l'avantage si on veut lancer plusieurs téléchargements depuis le même objet de ne pas avoir de souci (car sinon quand on implémente "connection:didReceiveData:" etc. alors qu'on a lancé plusieurs NSURLConnection avec le même delegate, comment savoir de quelle requête il s'agit ?)

    Bon, c'est vrai, c'est limité à  iOS4... Mais quand même, qu'est ce que c'est bien :D
  • 02:25 modifié #10
    C'est vrai que c'est puissant et que ça devient un peu (beaucoup?) la tendance depuis qu'Apple a présenté GCD. La preuve dans les nouveaux framework iOS4 (GameCenter et cie)
  • AliGatorAliGator Membre, Modérateur
    02:25 modifié #11
    Je viens pas plus tard que ce midi de l'utiliser encore sans m'en rendre compte (bon pour une appli mac sous OSX.6 mais bon) :)
  • muqaddarmuqaddar Administrateur
    02:25 modifié #12
    dans 1292937216:

    C'est vrai que c'est puissant et que ça devient un peu (beaucoup?) la tendance depuis qu'Apple a présenté GCD. La preuve dans les nouveaux framework iOS4 (GameCenter et cie)


    Tiens, je croyais que GCD avait du mal à  décoller... mais je dois confondre avec OpenCL qui a du mal à  devenir un standard ?
  • AliGatorAliGator Membre, Modérateur
    02:25 modifié #13
    En fait perso GCD je l'ai jamais utilisé explicitement (genre [tt]dispatch_sync[/tt], [tt]dispatch_async[/tt], [tt]dispatch_apply[/tt] et autres fonctions C de la libdispatch), mais je l'ai déjà  utilisé plusieurs fois implicitement rien qu'en utilisant NSOperation ou des trucs de NSArray comme [tt]enumerateObjectsWithOptions: usingBlock;[/tt] sous iOS4 ou OSX.6

    C'est l'API de haut niveau qui en fait sa force et qui fait qu'on l'utilise de façon relativement intuitive. Savoir qu'au lieu d'écrire
    for(id obj in monTableau) {<br />&nbsp; ... code ...<br />}
    
    Il suffit d'écrire
    [monTableau enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(...){<br />&nbsp; ... code ...<br />}];
    
    Pour transformer une boucle sur un tableau de boucle synchrone (itératif et en série) à  une boucle concurrente (où chaque itération est faite en parallèle).... sans avoir à  se poser des questions sur le multithreading ou avoir à  créer des NSThreads ou autre... c'est beau :P
  • muqaddarmuqaddar Administrateur
    02:25 modifié #14
    Sur un tableau de 8 items, il est assez intelligent pour envoyer 1 item à  travailler à  chaque core d'une architecture 8 cores donc, et sans rien de plus ? Ou il se contente de répartir sur plusieurs threads et une API de plus haut niveau gère les threads sur plusieurs cores ?
  • AliGatorAliGator Membre, Modérateur
    décembre 2010 modifié #15
    Ma réponse à  ta question : on s'en fout :P

    A vrai dire c'est GCD, donc il dispatch, sur différents threads, ces threads pouvant être répartis sur plusieurs cores, selon les priorités inférées par les threads créés et les autres opérations pouvant éventuellement être dispatchées sur la même dispatch_queue et tout... bref toute la mécanique du thread scheduling qui suit comme d'hab des règles et algos complexes pour un scheduling optimal en fonction des priorités des tâches et des temps CPU de chacun et des "nice" de chaque process tout en évitant les inversions de priorité et les gérant les dépendances et tout... bref la prise de tête... bah c'est GCD qui s'en charge !

    Tout ce qu'il y a à  savoir c'est qu'il faut au mieux, il se débrouille tout seul, y'a pas à  se prendre la tête c'est ça la beauté de GCD ! (Et si tu as déjà  eu des cours de multithreading tu sauras à  quel point c'est qqch qui peut soulager :P)
  • muqaddarmuqaddar Administrateur
    02:25 modifié #16
    C'est beau GCD. 
    ça donne envie de faire des applications bien optimisées du coup... (enfin, plutôt sur mac pour bien profiter des coeurs justement)

    dans 1292948676:

    (Et si tu as déjà  eu des cours de multithreading tu sauras à  quel point c'est qqch qui peut soulager :P)


    Si j'ai déjà  eu des cours de programmation plutôt... ah oui, en 2000 et 2001, un peu de C  à  l'IUT glande. Et c'est tout.  ;D
Connectez-vous ou Inscrivez-vous pour répondre.