Est-ce qu'il y a une limite à  la conso CPU?

HerveHerve Membre
octobre 2015 modifié dans API AppKit #1

Bonjour,


 


Mon logiciel d'animations me cause toujours du soucis. Lorsque je produis le film, le soft écrit sur le HD des images avant de les monter dans un film.


 


Lors de la création de ces images, le plus souvent, l'ordi crash (pas le soft, l'OS, qui redémarre). Lors des tests, je me rend compte que la conso énergie oscille entre "high" et "very high", et que la conso CPU dépasse les 80% La conso mémoire est maitrisée (autours de 120 Mo, voir d'autres discussions sur ce forum). J'utilise de très nombreux filtres CIImage/CoreImage à  chaque frame vidéo (images fractales oblige).


 


Est-ce que selon vous cette importante conso CPU est à  l'origine du crash systématique? (C'est mon analyse...) Dans ce cas, y a t-il des trucs pour la contrôler, à  l'image de NSOperation pour la conso mémoire?


 


Merci par avance pour toute aide...  


 


 


PS : en démarrant "à  froid", au démarrage de l'ordi, la création du film, cela marche mieux souvent, mais pas toujours.


«13

Réponses

  • Les filtres CIImages sont censés être des shaders traités par la carte graphique et non le CPU, non ?

  • AliGatorAliGator Membre, Modérateur
    Clairement si ton ordi chauffe trop tu as des sondés de températures qui passées un seuil de chaleur déclenchent les ventillos puis passés un pic de chaleur jugé trop important et surtout dangereux pour les composants, va faire redémarrer ta machine pour que tout s'arrête en urgence et sauver le hardware et éviter qu'il crame.


    Si pendant ta phase de traitement tes ventillos sont à  fond les ballons avant qu'il reboot c'est clairement ça. En plus tu dis que si tu démarres à  froid ça passe mieux donc ça tend à  confirmer cette hypothèse.
  • Deux mots alors: Tournevis, aspirateur. 


  • Ou aller dans un AppStore et demander un nettoyage en profondeur. C'est gratuit si je me souviens bien.

  • MalaMala Membre, Modérateur

    Je doute que le soucis vienne du côté CPU. La stabilité côté GPU est beaucoup plus précaire d'autant que pour le suivi mémoire on a pas vraiment d'outils de contrôle. 


  • CéroceCéroce Membre, Modérateur
    octobre 2015 modifié #7

    Je suis d'accord avec Mala. Comme il le dit, il n'existe pas de ségrégation de la mémoire vidéo, comme elle existe pour la mémoire CPU. (c'est d'ailleurs un problème de sécurité évoqué au sujet de WebGL).


     


    On parvient très facilement à  faire planter le Mac en programmant des filtres Core Image. Il n'y a qu'à  voir comment on parvient à  corrompre l'affichage des autres applications.


  • OK, merci beaucoup pour vos réponses, elles confirment mon intuition.


     


    L'idée de nettoyer l'intérieur du Mac ne m'était pas venue à  l'esprit : c'est vrai qu'il est ancien (2008 ou 2009). Il travaille avec un "bon vieux" Core2Duo. Depuis le temps, la poussière s'y est peut-être accumulée. Je vais voir avec l'Apple Store Opéra qui n'est pas très loin de chez moi.


     


    J'ai vraiment besoin pour mon projet de mes filtres CoreImage, mais avec mes fractales, cela fait facilement 50 à  100 filtres par image, voire plus (...), aussi lorsque le soft produit les images en série, aussi bien la carte graphique que le processeur principal sont trop sollicités sans doute? Est-ce qu'il y a un moyen de ralentir le processus pour laisser les processeurs souffler un peu ? (un Timer par exemple, mais en relation avec un contrôle de la "surchauffe"?)


     


    Merci encore en tous les cas.


  • MalaMala Membre, Modérateur

    Mon billet que cela ne va rien changer. 


     


    Déjà  la première chose à  faire, c'est de vérifier qu'Instrument n'est pas à  la ramasse au niveau CPU. Tu lances le moniteur d'activité et tu contrôles la conso mémoire réelle de ton process. Je parle en connaissance de cause car j'ai déjà  eu des surprises avec notamment des contextes graphiques qui n'étaient pas tracés par Instrument.


     


    Après, tu enfifonnes comment les images de ton flux vidéo? Tu restes dans le mainthread?

     


    Mais à  vue d'oeil, je dirais soit une fuite soit un débordement au niveau GPU. Et dans ces deux cas, tu peux te la mettre derrière l'oreille pour qu'instrument ou le moniteur d'activité te donne quoi que ce soit. Contrôles-tu bien tous les retours en erreur de l'API? 

  • HerveHerve Membre
    octobre 2015 modifié #10

    Merci Mala,


     


    Effectivement, je viens de tester un 



    sleep(1);

    dans le thread, cela a bien réduit la conso CPU (qui a oscillé autours de 30%), mais cela a planté quand-même passé 150 images ( Xcode a planté cette fois, pas l'OS... )


     


    Pour le reste, j'ai du mal à  comprendre ce que tu écris. (Sorry!) Les images du film sont crées via un NSOperationQueue dans un NSOperation pour ce qui concerne l'écriture sur le disque, mais la NSOperation appelle la classe de dessin habituelle, dérivée de NSView.


     


    Voici le code de la NSOperation :



    - (id)initWithNumber:(int)startPlan etNumber:(int)endPlan etDessin:(Dessin *) unDraw etNomFichier:(NSString *)strNom{

    if (self = [super init]){
    [self setLeDessin:unDraw]; //classe qui dérive de NSView, où est produit le dessin, et où sont appelés les CIFilters.
    [self setPlanInit:startPlan]; //pour le cas où on veut sortir un extrait du film
    [self setPlanFin:endPlan];
    [self setNomFichier:strNom]; //pour écriture des fichiers
    }

    return self;
    }

    - (void)main {
    // a lengthy operation
    int nbImExtrait = planFin - planInit;


    @autoreleasepool {
    for (int i = 0; i < nbImExtrait; i++){

    if ([self isCancelled]) {
    break;
    }
    @autoreleasepool {
    [leDessin setPlanEnCours:(i + planInit)];
    NSString *fileImage = [nomFichier stringByAppendingString:[NSString stringWithFormat:@_%d.tiff, (i + planInit)]];
    [leDessin creeUneVue:fileImage];//méthode de dessin, c'est elle qui écrit l'image en fait sur le HD.
    //on peut appeler ici le NSData/pdf pour l'écrire dans NSOperation, cela n'est pas mieux
    sleep(1);//inutile en fait

    } //fin du second pool
    }
    }

    [[NSNotificationCenter defaultCenter] postNotificationName: @imagesOntEteCrees
    object:self];
    //cette notification appelle une méthode qui utilise QTMovie pour monter les images dans un film.
    }

  • CéroceCéroce Membre, Modérateur

    Je ne te cache pas que pour générer des images fractales, je ne passerais pas par Core Image, qui sera probablement assez peu performant, et qui est limité à  la précision des floats. J'irais écrire directement les octets dans la bitmap, d'autant plus que ça se parallélise très bien.


  • HerveHerve Membre
    octobre 2015 modifié #12

    Pour ceux qui savent lire ces choses, je joins le dernier rapport de plantage (tout frais!   ::) )


  • MalaMala Membre, Modérateur
    octobre 2015 modifié #13

    Tu me fais un peu peur avec tes commentaires sur NSView dans ta sous classe de NSOperation Herve. Rassures moi, tes NSOperations sont bien lancées dans la mainQueue (donc le thread principal)? Car tout ce qui tourne autour de NSView n'est pas du tout thread safe. Et Core Image idem.


  • Ben à  vrai dire, je ne vois pas comment te rassurer...


     


    La classe "Dessin" qui dérive de NSView est chargée d'afficher les jolies images à  l'écran. Comme toutes les méthodes graphiques (qui incluent l'appel à  de nombreux filtres images : on importe des photos ou des bouts de photos qui sont colorisées, agrandies/rapetissées, déplacées, etc. par Core Image) sont dans la classe "Dessin", je fais appel à  elle pour récupérer un pdf qui est enregistré sur le disque. J'ai testé, soit "Dessin" crée et écris le fichier sur le HD, soit "Dessin" crée le fichier qu'écrit le NSOpération. Dans les deux cas, "BOUM!!!"


     


    Ceci dit, j'ai beaucoup appris en programmation en fonçant droit dans le mur sans les freins. Mes synthés sont très stables maintenant. Je commence seulement dans l'image animée... 


     


    Seulement effectivement, y'a un gros bug pas sympa. J'peux pas faire mes films. Des bouts comme celui-ci, pour vous montrer, que je monte ensuite, mais ce doit être plus efficace :


    http://www.hervenoury.com/Ph1C.mov


     


    Les vagues en bas et les nuages en haut sont des fractales (IFS de 3 transformations avec 3 itérations, soit 27 images chaque. Ceci n'est encore qu'un test.)


     


    La NSOperation est lancée en appelant une classe dérivée de NSOpération :



    NSOperationQueue *myQueue = [[NSOperationQueue alloc] init];
    myQueue.name = @CreeLesImages;
    myQueue.maxConcurrentOperationCount = 3;
    operationImages = [[OperationImages alloc]initWithNumber:planInit etNumber:planFin etDessin:leDessin etNomFichier:nomFichier]; //OperationImages dérive de NSOperation
    [myQueue addOperation:operationImages]; 

    "operationImages" écrit alors les images à  monter dans un deuxième temps sur le disque dur. (voir plus haut)


  • MalaMala Membre, Modérateur

    Je te proposerais bien un truc, c'est d'essayer en remplaçant juste le code suivant



    NSOperationQueue *myQueue = [[NSOperationQueue alloc] init];

    par 



    NSOperationQueue *myQueue = [[NSOperationQueue  mainQueue] retain];

    Si cela fonctionne, tu seras fixé. Cela incriminera le parallélisme.


  • Merci Mala pour ton idée. Je devine ce que tu proposes, j'essaie cela demain. Qu'appelles-tu cependant le "parallélisme"???


     


    En ce qui concerne le coup d'aspirateur dans mon Mac, l'Apple Store Opéra m'a renvoyé, vu que mon Mac n'est plus sous garantie, vers les centres agréés Mac... (L'un d'entre vous en particulier voulait savoir.) Je vais peut-être apprendre à  la faire moi-même? Auriez-vous un lien? (Ah, mon Mac, t'opérer... :'(  )


  • CéroceCéroce Membre, Modérateur


    Qu'appelles-tu cependant le "parallélisme"???




    Les traitements sur des threads séparés.

  • MalaMala Membre, Modérateur

    Ma crainte ici c'est que, comme il y a de l'accès UI au niveau des NSOperation, on risque de se retrouver dans une situation de lock récursif en remontant sur le main thread. A voir donc si ça passe.


  • Bonjour,


     


    Merci Céroce et Mala pour vos posts. 


     


    Je viens de faire le test. J'ai été obligé de maintenir mon " sleep(1);" dans mon autoreleasePool, sinon la conso CPU repartit vers des sommets. Ceci dit, en faisant :



    NSOperationQueue *myQueue = [NSOperationQueue mainQueue]; //c'est ça qui est nouveau
    myQueue.name = @CreeLesImages;
    myQueue.maxConcurrentOperationCount = 3;
    operationImages = [[OperationImages alloc]initWithNumber:planInit etNumber:planFin etDessin:leDessin etNomFichier:nomFichier];
    [myQueue addOperation:operationImages];

    j'ai pu sortir une séquence de 695 images sans casse ni crash. Il faut environ 3,5 secondes par images du coup (moyenne). La conso RAM augmente toujours un peu mais reste raisonnable (de 60Mo en temps normal, elle monte à  150Mo), et le CPU oscille entre 0% et 50%. 


     


    Je n'ai pas bien compris tes explications Mala. Aurais-tu un lien vers de la doc pour que je comprenne un peu? Là , j'avoue, je découvre...


     


    Avez-vous une idée sinon de comment "on pourrait faire mieux"? Merci par avance...


  • CéroceCéroce Membre, Modérateur


     


    Avez-vous une idée sinon de comment "on pourrait faire mieux"? Merci par avance...




     


     




    Je ne te cache pas que pour générer des images fractales, je ne passerais pas par Core Image, qui sera probablement assez peu performant, et qui est limité à  la précision des floats. J'irais écrire directement les octets dans la bitmap, d'autant plus que ça se parallélise très bien.



  • Ouuhh! C'est une idée, mais concrètement :


     


    J'imagine que tu sors en bitmap les photos d'origine, que tu récupères les valeurs rgb pour les repositionner dans le bitmap en cours? Tu fais tout en bitmap en somme? (j'avais commencé avec NSBitmapRep, mais cela ramait rien que pour recoloriser l'image, aussi CoreImage m'avait semblé plus rapide).


  • CéroceCéroce Membre, Modérateur

    J'avais fait une présentation aux CocoaHeads de Paris il y a des années.


     


    http://www.renaudpradenc.com/cocoaheads/#bitmaps


     


    Le code date un peu, mais ça doit toujours compiler.


  • Merci pour le lien, je vais lire cela.


    Pour ma part, j'avais fait un générateur de Mandelbrot (non publié) que je vais d'ailleurs updater en me servant des classes et méthodes nouvelles créées depuis puisqu'il prévoit de faire des animations. En effet, pour ce générateur, je travaillais directement au niveau de la bitmap, sans faire appel à  CoreImage. Ceci venait du fait qu'il fallait faire une série de calculs pixel par pixel. Ce sera le moment de vérifier si NSOperation est plus stable alors... 


  • MalaMala Membre, Modérateur
    octobre 2015 modifié #24


    Bonjour,


     


    Merci Céroce et Mala pour vos posts. 


     


    Je viens de faire le test. J'ai été obligé de maintenir mon " sleep(1);" dans mon autoreleasePool, sinon la conso CPU repartit vers des sommets. Ceci dit, en faisant :



    NSOperationQueue *myQueue = [NSOperationQueue mainQueue]; //c'est ça qui est nouveau
    myQueue.name = @CreeLesImages;
    myQueue.maxConcurrentOperationCount = 3;
    operationImages = [[OperationImages alloc]initWithNumber:planInit etNumber:planFin etDessin:leDessin etNomFichier:nomFichier];
    [myQueue addOperation:operationImages];

    j'ai pu sortir une séquence de 695 images sans casse ni crash. Il faut environ 3,5 secondes par images du coup (moyenne). La conso RAM augmente toujours un peu mais reste raisonnable (de 60Mo en temps normal, elle monte à  150Mo), et le CPU oscille entre 0% et 50%. 


     


    Je n'ai pas bien compris tes explications Mala. Aurais-tu un lien vers de la doc pour que je comprenne un peu? Là , j'avoue, je découvre...


     


    Avez-vous une idée sinon de comment "on pourrait faire mieux"? Merci par avance...




     


    Pourquoi diable veux-tu encore limiter ton CPU avec ce sleep? Laisses la bosser ta machine. Elle est faite pour ça. Ce qui est important ici c'est de bien comprendre que si cela ne plante plus cela confirme que le souci vient bien d'un problème de codage non thread safe. Et vu que ça part en kernel panic, c'est que c'est très très vraisemblablement au niveau GPU. Donc laisses tomber le dépoussiérage. C'est pas dans la machine qu'il faut le faire mais bien dans le code.


     


    Au niveau doc, c'est écrit un peu partout dans la documentation d'Apple: les apis graphiques (les views dans la notion de MVC) ne sont pas sécurisées pour un accès concurrent par plusieurs threads en même temps. Hors dans ton usage, c'est exactement ce que tu sembles faire puisque tes NSOperation étaient lancées en parallèle (c'est un peu le but des NSOperation à  la base) et que tu semblais taper sur des views d'après tes commentaires. Et malheureusement, je pense pas trop m'avancer en écrivant sans le vérifier que Core Image est pas non plus thread safe.


     


    Pour faire mieux, je dirais qu'il faut déjà  clarifier les choses dans ton esprit en terme de conception. D'ailleurs au niveau MVC, tu te mords la queue puisque dans la logique des choses tes NSOperations feraient plutôt partie du model dans ton cas et elle ne devrait donc en aucun cas manipuler directement des views.


     


    Donc règle numéro 1: tout code manipulant une partie d'interface graphique (je n'ai toujours pas compris ce que tu fais avec tes views ici) doit être exécuté dans le main thread.


     


    Après oui, on peut partir sur des manipulations directes de pixels qui seront beaucoup beaucoup plus performantes même en mono coeur et réellement parallélisable (là  faut accrocher son string car la machine monte à  100% de tous les coeurs donc 8 coeurs par exemple c'est 800% d'usage CPU et si c'est bien codé la machine ne bronche pas) mais j'ai peur que tu ne sois pas prêt si déjà  en l'état les notions précédentes t'échappes.


  • MalaMala Membre, Modérateur
    octobre 2015 modifié #25

    Oui, je n'ai pas écrit de bêtise sur Core Image semble-t-il...


     



    Maintaining Thread Safety



    CIContext and CIImage objects are immutable, which means each can be shared safely among threads. Multiple threads can use the same GPU or CPU CIContext object to render CIImage objects. However, this is not the case for CIFilter objects, which are mutable. A CIFilter object cannot be shared safely among threads. If your app is multithreaded, each thread must create its own CIFilter objects. Otherwise, your app could behave unexpectedly. 




     


    Source: https://developer.apple.com/library/ios/documentation/GraphicsImaging/Conceptual/CoreImaging/CoreImage.pdf


  • HerveHerve Membre
    octobre 2015 modifié #26

    Merci Mala pour ton long post. Je viens encore de faire un joli crach de l'OS sous Xcode (!!) malgré mon "sleep(1)". 


     


    Je commence moi-même à  te comprendre. Je ne sais toujours pas ce que vous appelez "thread safe", "parallélisme", etc. Mais MVC, je sais. 


    La dérivée de NSView est chargée de faire les images tout simplement. Sa méthode "drawRect" contient l'algorithme  des IFS et des itérations d'images recollées et déplacées à  l'écran via CoreImage. Elle crée des tableaux de valeurs (qui concernent la couleur, la taille, le positionnement, etc.) en fonction du nombre et de la nature des itérations, qui vont ensuite être utilisées par les filtres. Cette méthode "drawRect" est appelée - sans que l'image écran ne s'affiche et ne soit mise à  jour cependant", lorsque je créer le fichier .tiff pour l'écrire sur le HD :



    - (NSData *) donneLaVue{
    NSSize imgSize = self.bounds.size;

    NSBitmapImageRep * bir = [self bitmapImageRepForCachingDisplayInRect:[self bounds]];
    [bir setSize:imgSize];

    [self cacheDisplayInRect:[self bounds] toBitmapImageRep:bir]; //c'est ici que drawRect est rappelé

    laVue = [[NSImage alloc] initWithSize:imgSize];
    [laVue addRepresentation:bir];

    [self setPdfData:[laVue TIFFRepresentation]]; //NSData *pdfData est une variable de classe
    return pdfData;
    }

    C'est donc cette méthode qu'il faudrait apparemment réécrire, ou bien, si je vous comprends bien, il faudrait recopier toute la méthode de dessin dans la dérivée de NSOperation sans faire appel à  la dérivée de NSView ???? Je vais voir ce que je peux faire, mais cela m'étonne... Ou bien créer les images dans Dessin? (y écrire la boucle?)


     


     


    En tous les cas, la doc de Core Image est très claire en effet.


  • MalaMala Membre, Modérateur
    octobre 2015 modifié #27

    Déjà  oublies ton sleep il ne fait que ralentir l'inéluctable. Ton dernier plantage en l'état c'est quoi? Violation d'accès ou kernel panic?


     


    Thread safe: code qui peut être exécuté en même temps  sans risque par plusieurs threads (donc en parallèle dans le jargon).


     


    Par exemple dans ton cas plusieurs NSOperations lancées en parallèle et qui accèdent aux même données (ton film). Si chaque NSOperation peut "déplacer" la position du film pour aller à  l'image suivante, tu peux te retrouver avec une NSOperation qui accède à  une image et une autre qui bouge la "bande" en même temps. Ca risque de partir en couille. Ce n'est pas thread safe.


     


    Ensuite n'allons pas trop vite car nous on connait pas ton code si ce n'est quelques bribes.


     


    Tu projettes les images de ton film dans une NSView pour les capturer? C'est bien ça? Je trouve ça un peu tordu non? Il n'y a pas plus simple? -ceci dit QuickTime a été tellement bricolé et rebricolé par Apple que je ne serais pas étonné que la réponse soit non-  Mais effectivement en l'état c'est pas thread safe ça.


  • HerveHerve Membre
    octobre 2015 modifié #28

    Je n'ai pas conservé le message de crash. Oups! Je viens d'essayer de faire écrire les images par la NSView, pas mieux : crash au bout de 330 écritures. Là  encore, pas pris soin de copier le rapport. Ne sachant pas vraiment les lire, je n'ai pas acquis le bon réflex... 


     


    Je vous montre le projet zippé, pour ceux que le sujet intéresse. L'idée est de paramétrer des transformations du plan pour chaque image avec les sliders à  droite de l'image. Je joins aussi un exemple d'utilisation.


     


    Voir PJ


     


    ​Les premières classes (Anima-Film, Anima-Image, etc.) sont les classes qui stockent les données via NSCoding. Document contient l'ensemble des réponses aux menus et sliders. Dessin est la NSView. ColorisateurImage le filtre CoreImage.OperationImages la NSOperation.


     


    Je ne comprends toujours pas "où sont" les threads. Un thread est-il par exemple le dessin d'un côté, et l'écriture de l'autre (ce qui revient à  dire que l'on ne peut pas écrire sur le disque dur en utilisant CoreImage, ce qui est absurde...) ou bien l'encapsulation dans une classe des méthodes (mais même en faisant tout faire par "Dessin", il y a crash). Où - ou quand - s'arrête un thread en somme?

  • HerveHerve Membre
    octobre 2015 modifié #29

    Bonjour,


     


    Je ne sais pas si vous êtes par là  le dimanche... 


     


    J'ai relu ma doc sur les threads : j'en gardais un petit souvenir de l'époque où je faisais du Java. C'est bien le souvenir que j'en avais. Ceci dit, où sont les thread dans mon processus?


     


    Je viens de tester l'écriture des images depuis la NSView, sans le "@autoreleasepool", pour voir. La conso RAM monte des 3,1 Mo par image, ce qui est attendu, mais l'appli crash l'OS encore. Cette fois, j'ai gardé le rapport (en PJ). 


     


    En quoi le fait d'écrire des images sur le HD en faisant appel à  NSData crée plusieurs threads? L'appli est stable lorsqu'on travaille avec pour créer les plans. Elle ne crash pas. D'où vient le problème selon vous? Faut-il utiliser une autre méthode pour écrire les images? 


     


    Je précise : le crash a lieu lors de la création des images. L'écriture du film par QTMovie n'a jamais posé de problème. L'appli crash passé une centaines d'images écrites sur le HD.


  • Deux éléments de réflexions :


    - quand mon GPU plante, l'ordinateur freeze et je dois le redémarrer brutalement.


    - pour ce que tu fais, as-tu envisagé OpenCL ?


     


    Il y a un exemple que je trouve super qui montre quelque chose de proche (les ensembles de Julia).


     


    Le code C, mais aussi le code OpenCL sont très simples à  comprendre. Bon, c'est sûr qu'il y a tout une partie du code qui met OpenCL en place, mais c'est malgré tout assez logique à  comprendre. 


     


    https://developer.apple.com/library/mac/samplecode/OpenCL_RayTraced_Quaternion_Julia-Set_Example/Introduction/Intro.html


     


    En plus, les résulats sont affichés à  l'aide d'OpenGL, ce qui permet de voir l'articulation OpenCL/OpenGL.


  • HerveHerve Membre
    octobre 2015 modifié #31

    Je ne connaissais pas OpenCL. Merci.


     


    Ceci dit, j'aimerais tout de même comprendre si le problème actuel vient du "multi-thread", car je ne vois pas où de multiples instructions sont lancées. Il n'y a qu'une seule boucle! Et je ne comprends vraiment rien au "crash report", j'avoue!


     


    J'ai bien trouvé cela, mais bon... :


    http://www.thexlab.com/faqs/kernelpanics.html


Connectez-vous ou Inscrivez-vous pour répondre.