Traitement XML et conso CPU/Mémoire

hdexhdex Membre
octobre 2006 modifié dans API AppKit #1
Après avoir lu Tuto sur NSXML, j'ai décidé de tater du XML ... et la mon TiBook  :brule:

En executant le code suivant (ouverture de mon fichier XML), ma CPU grimpe a 100% et la conso mémoire passe rapidement de 40Mo a 200 Mo (mon fichier XML fait une cinquantaine de megs).
<br />    NSXMLDocument *xmlDoc;<br />    NSError *err=nil;<br />    NSURL *furl = [NSURL fileURLWithPath:file];<br />    if (!furl) <br />	{<br />        NSLog(@&quot;Can&#39;t create an URL from file %@.&quot;, file);<br />        return;<br />    }<br />    xmlDoc = [[NSXMLDocument alloc] initWithContentsOfURL:furl<br />            options:(NSXMLNodePreserveWhitespace|NSXMLNodePreserveCDATA)<br />            error:&amp;err];<br />    if (xmlDoc == nil) <br />	{<br />        xmlDoc = [[NSXMLDocument alloc] initWithContentsOfURL:furl<br />                    options:NSXMLDocumentTidyXML<br />                    error:&amp;err];<br />    }<br />    if (xmlDoc == nil)  <br />	{<br />        if (err) <br />		{<br />            //[self handleError:err];<br />			NSLog(@&quot;ERROR !!&quot;);<br />        }<br />        return;<br />    }<br /> <br />    if (err) {<br />        //[self handleError:err];<br />		NSLog(@&quot;ERROR !!&quot;);<br />    }<br />	[xmlDoc release];<br />


Pour l'instant mon but est simple, ouvrir le fichier XML ...
J'ai essayé d'utiliser NSXMLParser (lecture du fichier et delegate faisant un NSLog des elements/attributs) et j'ai le même genre de problème.

J'avoue que là  je sèche, soit mon TiBook est anti-XML soit je fait une grosse boulette (bizarrement je penche pour la 2ème solution)

Edit : J'ai laissé tourner pendant un moment ... 22 minutes plus tard, la lecture du fichier s'est terminé. CPU à  0 mais toujours 260Mo alloué pour mon appli ... Y doit y avoir comme un problème là  !!  :crackboom:-

Réponses

  • tarultarul Membre
    02:55 modifié #2
    ce que je vais te dire vient de mon experience en java et dans d'autres langages. En objective-c Cocoa, je ne sais pas si c'est la même chose, mais je pense qu'il doit y avoir des similitudes.

    lorsque je veux lire ou faire un traitement sur un xml avec java j'ai en gros 2 familles d'api. Celles dites "DOM" et celle dites "SAX". la première charge entierement le fichier xml en mémoire en crée un arbre. Elle est est la plus consomatrice en therme de ressources. mais en java je la trouve plus simple.
    La seconde se base sur un gestionnaire d'evenement, là  en fonction des évenements qui te sont transmis, tu fait tel ou tel traitement. Normalement c'est la moins consomatrice en ressource. En effet elle ne charge pas le fichier entierement pour en faire un arbre.

    Je ne sais pas si cela va t'aider. Au fait il y a quoi dans ton fichier, que veux-tu en faire?
  • AliGatorAliGator Membre, Modérateur
    02:55 modifié #3
    dans 1159800593:
    ce que je vais te dire vient de mon experience en java et dans d'autres langages. En objective-c Cocoa, je ne sais pas si c'est la même chose, mais je pense qu'il doit y avoir des similitudes.
    Juste pour info, c'est la même chose avec les API Cocoa que ce que tu décris pour Java :
    - les classes NSXMLDocument, NSXMLElement, NSXMLNode & co utilisent le DOM : tu charges le document XML puis tu navigues dedans node par node. (ou le crée node par node from scratch)
    - NSXMLParser fonctionne en tant que parseur SAX (évènementiel) et appelle des méthodes de delegate au fur et à  mesure qu'il rencontre des nodes dans le XML qu'il parse
  • hdexhdex Membre
    02:55 modifié #4
    Merci pour les infos. J'ai un peu plus potasse les docs et je comprends un peu mieux ... juste un peu  ::)

    Mon fichier est utilise en interne par ma boite afin de fournir des references dans le secteur de l'assurance immobiliere. En gros, le fichier contient des titres d'articles, des auteurs, des dates et un (ou des liens) vers des URL. Je fais mumuse avec, histoire de voir si nos graphistes sous Mac peuvent avoir acces a ces infos.

    Pour revenir au parseur, je pense utiliser NSXMLParser car je veux juste recuperer une partie des infos au fur et a mesure de la lecture du fichier. NSXMLDocument consomme un peu trop de ressources sur ma machine de toute facon.

    En utilisant NSXMLParser ma CPU tourne autour de 5-10%, la lecture du fichier se termine en 1-2minutes mais ma conso memoire pour l'appli monte a 150Mo (je ne stocke rien en memoire, mes delegate font juste des NSLog) ... je commence donc a me demander, si le Moniteur d'activite me donne les bonnes infos ... yaurait-il une commande ou un outil pour verifier la conso memoire de son appli ? (top me renvoi la meme info) ?
  • tarultarul Membre
    02:55 modifié #5
    normal que le moniteur te renvoi les valeur de ton top. il me semble que ce dernier se base sur lui.

    Sinon, ne te serait-il pas possible d'accéder a une BDD? Je pense que pour un accés partagé a des données, et pour une base consomation ce serait mieux non?
  • MathMath Membre
    02:55 modifié #6
    Pour savoir combien ton apli prend en mémoire, compile ton projet en release, ferme xcode et lance ton application en cliquant deux fois sur son icone.

    L'application prend plus de rame qd elle est lancé depuis xcode.
  • hdexhdex Membre
    02:55 modifié #7
    Merci, merci, merci  :P

    J'ai fait quelques tests suivant vos recommandations, et j'ai appris quelques trucs de bases :
    - Pour voir la véritable conso mémoire, ne pas faire tourner l'appli sous XCode
    - utiliser des NSLog c'est bien mais pas trop... en particulier si on n'a pas pris soin de suivre la règle précédente (ma CPU grimpait au rideau à  cause de XCode qui passait son temps a traiter les NSLog).

    Même avec ça en tête, mon appli continuait de vampiriser 200Mo de RAM sans les libérer une fois le parseur terminé.
    Pour voir si le problème venait de mon code, j'ai donc essayé de passer le fichier xml de iTunes à  ma moulinette NSXMLParser (356Ko ... on ne rigole pas merci  ;)).
    Et là  pas de soucis, CPU et RAM grimpe un peu et reviennent a des valeurs normales.

    Ahaha, c'est donc mon fichier ... pour en avoir le coeur net j'ai fait un autre tests sur des fichiers xml de taille diverses (trouvé sur http://www.ximpleware.com/benchmark1.html)
    Résultat, dés que le fichier xml fait plus de quelques Mo (un fichier d'adresses de 15Mo par exemple), CPU et RAM montent en flèche. L'aspect CPU ne m'inquiète pas, c'est plutôt la mémoire qui m'ennuie (pour 15Mo de donnée a parser):
    - 4 Mo au lancement
    - 111 Mo en plein parsing
    - 67Mo à  la fin

    Release incomplet ou code spécifique pour NSXMLParser quand les fichiers sont trop gros ? Si quelqu'un à  une idée je suis preneur ....

    Pour infos mon code
    <br />@implementation HDAppController<br /><br /><br />- (void)parseXMLFile:(NSString *)pathToFile <br />{<br /> 	NSLog(@&quot;parseXMLFile&quot;);<br />	BOOL success;<br />&nbsp; &nbsp; NSURL *xmlURL = [NSURL fileURLWithPath:pathToFile];<br />	<br />	NSXMLParser *catParser = [[NSXMLParser alloc] initWithContentsOfURL:xmlURL];<br />&nbsp; &nbsp; <br />	NSLog(@&quot;addressParser&quot;);<br />	[catParser setDelegate:self];<br /><br />&nbsp; &nbsp; [catParser setShouldResolveExternalEntities:YES];<br />&nbsp; &nbsp; success = [catParser parse]; // return value not used<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // if not successful, delegate is informed of error<br />	<br />	NSLog(@&quot;End&quot;);<br />	[catParser release];<br /><br />}<br /><br /><br />- (IBAction)parseGutCatalog:(id)sender<br />{<br /><br />	//NSString *filePath = [@&quot;~/Music/iTunes/iTunes Music Library.xml&quot; stringByExpandingTildeInPath];<br />	NSString *filePath = [@&quot;~/Documents/Downloads/xmls/address.xml&quot; stringByExpandingTildeInPath];<br />	NSLog(@&quot;%@&quot;, filePath);<br />&nbsp;  [self parseXMLFile:filePath];<br />}<br /><br /><br />- (void)parserDidStartDocument:(NSXMLParser *)parser<br /><br />{<br /><br />&nbsp; &nbsp; NSLog(@&quot;Parser Start&quot;);<br />}<br /><br /><br /><br />- (void)parserDidEndDocument:(NSXMLParser *)parser<br /><br />{<br /><br />&nbsp; &nbsp; NSLog(@&quot;Parser End&quot;);<br />	<br /><br />}<br /><br /><br />- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict<br /><br />{<br /><br />&nbsp; &nbsp; NSLog(@&quot;=== START %@ tag ====&quot;, elementName);<br /><br /><br />&nbsp; &nbsp; if ([attributeDict count] &gt; 0)<br />	{<br /><br />&nbsp; &nbsp; &nbsp; &nbsp; NSLog(@&quot;--- === Attributes === ---&quot;);<br /><br /><br />&nbsp; &nbsp; &nbsp; &nbsp; NSEnumerator *enumerator = [attributeDict objectEnumerator];<br /><br />&nbsp; &nbsp; &nbsp; &nbsp; id value;<br /><br />&nbsp; &nbsp; &nbsp; &nbsp; while ((value = [enumerator nextObject])) <br />		{<br /><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; NSLog(@&quot;&nbsp; %@&quot;, value);<br /><br /><br />&nbsp; &nbsp; &nbsp; &nbsp; }<br /><br />	<br />&nbsp; &nbsp; }<br />	<br /><br />}<br /><br /><br /><br />- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName<br /><br />{<br />&nbsp; &nbsp; NSLog(@&quot;=== END %@ tag ===&quot;, elementName);<br /><br />}<br /><br /><br /><br /><br />- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string<br /><br />{<br /><br />&nbsp; &nbsp; &nbsp;  // NSLog(@&quot;Found Characters = %@&quot;, string);<br /><br /><br />}<br /><br /><br />@end<br /><br />
    
  • zeuszeus Membre
    02:55 modifié #8
    Salut,

    j'avais eu le même problème que toi et sur ce fil de discution tu peux voir comment j'ai fini par le résoudre http://www.objective-cocoa.org/forum/index.php?topic=1713.0

    @+ Jérôme
  • hdexhdex Membre
    octobre 2006 modifié #9
    Merci Jérôme   

    La bonne nouvelle : j'ai mis un pool autorelease encadrant mon code parseXMLFile et la conso mémoire redescend (monte a 300Mo puis revient à  200Mo).

    La mauvaise nouvelle : ben c'est les 200-300Mo. Sauf si j'ai mal compris NSXMLParser, il devrait y avoir peu de ressources alloués étant donné que le fichier est lu au fur et à  mesure. J'ai fait un test de mon code sans aucun NSLog avec des delegate présent mais n'éxecutant aucun code. Même comme ça, ma conso mémoire reste élevée et surtout mon appli utilise 200Mo une fois que le parseur a fini.

    Je continue les tests en suivant les conseils de Jérôme ...

    Yaurait-il une astuce pour utiliser NSXMLParser avec des fichiers contenant beaucoup de tags ??

    Edit : un petit tour dans MallocDebug m'a appris que j'ai 2 fonctions qui utilise de la mémoire. L'une correspond à  l'ouverture du fichier et occupe grosso modo la même taille en mémoire que sur disque. Le second correspond au parsing et la l'utilisation mémoire grimpe à  cause de la création d'un tas de CFDictionnary et CFString ... même quand mon code ne fait rien.
  • hdexhdex Membre
    02:55 modifié #10
    :o :o :o :o

    Avec les conseils de Jerome et en y regardant de plus pres, le probleme vient de .... moi  :o

    En fait quand mon programme s'execute, la colonne 'real' du moniteur d'activite grimpe en fleche MAIS 'active memory' augmente seulement de quelques Mo.

    la commande leaks confirme aussi que mon programme utilise 800ko a la fin du parsing.

    En conclusion, en plus de comprendre les details de Cocoa, il aurait fallut que je potasse comment le moniteur d'activite fonctionne.

    Merci a tous pour vos conseils  o:)
  • AliGatorAliGator Membre, Modérateur
    02:55 modifié #11
    Hello

    A ce propos pourrais-tu donner plus de détails, si maintenant tu en as, concernant la "façon de lire les résultats du Moniteur d'Activité" ?
    En effet j'ai jamais su trop la différence entre les différents types de mémoire mentionnés (résidente, allouée, active...)  :o

    Merci  :P
  • hdexhdex Membre
    02:55 modifié #12
    Bon je vais essayer de resumer ce que j'ai cru comprendre (et desole pour les termes en anglais mon Mac OS est configure anglais)

    Wired (par defaut en rouge) : memoire reserve pour des taches internes de l'OS et non paginable
    Active (par defaut en jaune) : memoire allouee pour les appli
    Inactive (par defaut en bleu) : memoire anciennement utilise mais non liberee/paginee.

    Mac OS (ainsi que FreeBSD et surement tout les autres BSD), ne libere pas systematiquement la memoire. En clair, quand une appli utilise une zone memoire et la libere Mac OS passe la memoire en inactive. L'operation de nettoyage de cette zone ne se fait que s'il y a un besoin en memoire ou si l'appli se termine. En gros, Mac OS ne fait un nettoyage que si c'est utile tout en essayant de faire un maximum d'operation en memoire (d'ou une valeur de memoire libre toujours relativement faible)

    Dans mon cas la colonne 'Real' grimpe mais je pense que c'est seulement une indication de ce que mon appli pourrait alloue.

    Perso, je regarde les valeurs 'active' (memoire reellement utilise par Mac OS et les applis) et les pageins/out (pagination). Quand je fais tourne mon appli, 'active' augmente legerement et aucune pagination ne se fait ... j'en conclu donc que l'utilisation memoire est raisonnable.

    la meme chose en anglais : http://www.cocoabuilder.com/archive/message/cocoa/2005/3/29/131594
Connectez-vous ou Inscrivez-vous pour répondre.