Les membres ayant 30 points peuvent parler sur les canaux annonces, projets et hs du chat.
La shoutbox n'est pas chargée par défaut pour des raisons de performances. Cliquez pour charger.

Forum Casio - Autres questions


Index du Forum » Autres questions » Timer haute résolution gint/graph 90+E
Slyvtt En ligne Maître du Puzzle Points: 2422 Défis: 17 Message

Timer haute résolution gint/graph 90+E

Posté le 17/01/2022 22:01

Hello,

je voudrais me faire une boucle de mesure de perf de certaines opérations pour booster mes routines et être capable de mesurer les gains lors d'optimisations. Pour ce faire, je voudrais avoir une chaine de mesure de "Delta t" entre 2 points d'un programme.

J'ai codé ça, qui me semble pas trop trop mal fonctionner mais peut être y-a-t'il moyen de faire mieux (j'ai pas l'impression qu'on puisse descendre en dessous de 256Hz avec rtc.h, donc j'ai fait avec timer.h) :

#include <gint/display.h>
#include <gint/keyboard.h>
#include <gint/timer.h>

#define DELTA_INC 5                // set resolution at 5µs
                                                          // does not seem to work with lower values

static volatile uint16_t counter = 0;
uint16_t a, b;


static int callback_tick(volatile uint16_t *tick)
{
    *tick += DELTA_INC;
    return TIMER_CONTINUE;
}

int main(void)
{
    int t = timer_configure(TIMER_ANY, DELTA_INC, GINT_CALL(callback_tick, &counter));

    counter = 0;
    if(t >= 0) timer_start(t);
    a = counter;

    // operations to be evaluated
    // BEGIN

    for(int k=0;k<100;k++)
    {
        dclear(C_WHITE);
        dprint(1, 1, C_BLACK, "Sample fxSDK add-in.");
        dupdate();
    }

    // END

    b=counter;
    if(t >= 0) timer_stop(t);



    // Print the result in µs
    dclear(C_BLACK);
    dprint(1, 1, C_WHITE, "Time to operate : %lu micro-seconds.", counter );
    dprint(1, 10, C_WHITE, "Delta t (b-a)      : %lu micro-seconds.", (b-a) );
    dupdate();


    getkey();

    return 1;
}


Par contre, si je descends la résolution en dessous de 5µs pour la résolution, ça coince (le programme tourne en boucle sans faire les opérations d'affichage.

En fait, j'ai deux affichages (sauf erreur de compréhension de ma part, ce qui n'est pas exclu) :
- le premier, "counter" qui me donne le temps total entre timer_start() et timer_stop()
- et le second "delta (a-b)" qui retire le temps des opérations timer_start() et timer_stop()

Comme résultat, j'obtiens grosso modo 5µs de durée pour le couple timer_start() + timer_stop() et 7828µs pour la réalisation de 100 boucles (dclear/dprint/dupdate), soit ~78µs par tour.

Est-ce que cela vous parait plausible ?

Merci.

Sly


Dark storm Hors ligne Labélisateur Points: 11641 Défis: 176 Message

Citer : Posté le 17/01/2022 22:08 | #


Pourquoi ne pas utiliser la libprof ? C’est conçu pour
Finir est souvent bien plus difficile que commencer. — Jack Beauregard
Slyvtt En ligne Maître du Puzzle Points: 2422 Défis: 17 Message

Citer : Posté le 17/01/2022 22:13 | #


Juste parce que je connaissais pas libprof

du coup je vais regarder ;-)
mais en fait j'ai l'impression que mon algo ne fonctionne pas, car si j'accélère la calculatrice avec Ptune3, le temps de la boucle augmente ...
pas très logique.
There are only 10 types of people in the world: Those who understand binary, and those who don't ...
Lephenixnoir En ligne Administrateur Points: 24699 Défis: 170 Message

Citer : Posté le 17/01/2022 22:21 | #


Tu as plusieurs problèmes avec ce code. D'abord, un uint16_t c'est clairement trop petit ; ça ne te donne que 65.536 ms de temps de mesure, et ta boucle est beaucoup plus longue que ça. Donc les résultats sont clairement faux, tout est violemment réduit modulo.

Ensuite, le principe même de ta mesure a un problème fondamental. Ton incrémentation du compteur se fait par le biais d'un callback de timer, ce qui veut dire que toutes les 5 µs le processeur doit interrompre ton code pour émettre une interruption, gint doit traiter l'interruption, interagir avec le timer, et appeler ton callback qui incrémente la variable. Plus la fréquence de la mesure est élevée, plus ce travail prélève du temps sur le programme et déforme la mesure.

La raison pour laquelle ça freeze si tu mets moins c'est parce qu'en 4 µs le programme n'a pas le temps de traiter entièrement une interruption, et donc 4 µs après ton timer_start(), le programme se fait interrompre, suit la procédure que j'ai décrite et incrémente le compteur ; mais une fois la procédure terminée 4 µs se sont de nouveau écoulées, si bien que le même traitement redémarre immédiatement et ton programme ne peut jamais reprendre. Dans gintctl je mesure qu'on peut traiter environ 280'000 interruptions par secondes, ce qui revient à peu près à ça. Note que 4 µs ça ne fait que 500 cycles processeur, donc pas tant d'instructions que ça, c'est une échelle très courte.

Si 4 µs représente la fréquence à laquelle le traitement des interruptions occupe 100% du processeur, en te mettant à 5 µs tu te places volontairement le plus proche possible de ça, c'est-à-dire que dans ton programme le traitement des interruptions occupe certainement plus de 50-80% du temps de processeur, ce qui fausse complètement ta mesure encore une fois.

Si tu accélères avec Ptune3, le temps de la boucle change, et comme tout est réduit modulo 65.536 ms extrêmement agressivement tu as un résultat presque aléatoire.

-

Comme Dark Storm l'a indiqué, il existe une bibliothèque pour faire des mesures de performances avec gint, libprof. La différence entre libprof et ton code ici c'est que libprof va directement lire le compteur du timer et n'active jamais les interruptions. Ton idée générale d'utiliser un timer est tout à fait exacte ; il aurait fallu que tu saches que le compteur interne existe (et comment l'interpréter) pour avoir une mesure raisonnable.

Les mesures de libprof sont précises à un niveau inférieur à la micro-seconde, à tel point qu'avec un peu de répétition on peut mesurer les cycles d'instructions individuellement, ce dont j'admets être assez fier

Pour ce qui est de ton code, il faut savoir que sur la Graph 90+E le dessin prend du temps, beaucoup de temps par rapport au reste. C'est parce qu'il y a 80'000 pixels et 170 kio de données graphiques pour former une image (396×224 pixels de 16 bits chacun). Je t'invite à regarder les ordres de grandeur pour te faire une idée.

Ici, ta boucle prend 14 ms environ. (C'est même pas la peine de le répéter... la précision est largement suffisante !) Pour effacer la VRAM avec une boucle, il faudrait ~6 ms, mais dclear() utilise le DMA et termine en 2.5 ms. dupdate() est limité par la vitesse de l'écran et prend toujours 11 ms (*). Le texte occupe peu de surface, c'est négligeable.

Tu peux faire le même test avec libprof comme ceci :

prof_t ctx = prof_make();
prof_enter(ctx);
dclear(C_WHITE);
dprint(etc);
dupdate();
prof_leave(ctx);
int time_us = prof_time(ctx);

Ou dans le cas où le code à exécuter est assez simple pour passer en argument d'une macro :

int time_us = prof_exec({
  dclear(C_WHITE);
  dprint(etc);
  dupdate();
});

(*) L'arnaque avec dupdate() c'est que dupdate() lance le DMA pour démarrer la copie des données graphiques de la VRAM vers l'écran, mais n'attend pas qu'elle se termine. Le transfert se fait plus ou moins en parallèle du code qui suit. Donc attention au piège : dans les exemples que j'ai mis dupdate() ne sera pas compté, et en plus si tu essaies de mesurer les performances du code juste après un dupdate() le temps de copie peut s'en mêler.

En général on mesure le temps du rendu sans le dupdate() et c'est tout (puisque c'est ça le plus long) et ça se passe bien.

-

Quitte à faire de la pub, je me permets de lier ce topic, qui contient définitivement quelques techniques utiles pour toi
Mon graphe (11 Avril): ((Rogue Life || HH2) ; PythonExtra ; serial gint ; Boson X ; passe gint 3 ; ...) || (shoutbox v5 ; v5)
Slyvtt En ligne Maître du Puzzle Points: 2422 Défis: 17 Message

Citer : Posté le 17/01/2022 22:39 | #


Super, merci pour les explications, c'est parfaitement clair et je supputais une ânerie de ce genre.
Sur les bons conseils de Dark Storm, j'ai fait une install de LibProf et effectivement ca sert à rien de réinventer l'eau chaude

Je note pour la subtilité de dupdate().
Bon a priori ce qui m'intéresse c'est justement avant le dupdate(); je vais considérer que le dupdate() sera un offset de temps similaire à chaque exécution.

Je vais encore me coucher moins bête ce soir

Merci aussi pour le lien en fin de post, il y a plein de choses intéressantes en effet.
There are only 10 types of people in the world: Those who understand binary, and those who don't ...

LienAjouter une imageAjouter une vidéoAjouter un lien vers un profilAjouter du codeCiterAjouter un spoiler(texte affichable/masquable par un clic)Ajouter une barre de progressionItaliqueGrasSoulignéAfficher du texte barréCentréJustifiéPlus petitPlus grandPlus de smileys !
Cliquez pour épingler Cliquez pour détacher Cliquez pour fermer
Alignement de l'image: Redimensionnement de l'image (en pixel):
Afficher la liste des membres
:bow: :cool: :good: :love: ^^
:omg: :fusil: :aie: :argh: :mdr:
:boulet2: :thx: :champ: :whistle: :bounce:
valider
 :)  ;)  :D  :p
 :lol:  8)  :(  :@
 0_0  :oops:  :grr:  :E
 :O  :sry:  :mmm:  :waza:
 :'(  :here:  ^^  >:)

Σ π θ ± α β γ δ Δ σ λ
Veuillez donner la réponse en chiffre
Vous devez activer le Javascript dans votre navigateur pour pouvoir valider ce formulaire.

Si vous n'avez pas volontairement désactivé cette fonctionnalité de votre navigateur, il s'agit probablement d'un bug : contactez l'équipe de Planète Casio.

Planète Casio v4.3 © créé par Neuronix et Muelsaco 2004 - 2024 | Il y a 76 connectés | Nous contacter | Qui sommes-nous ? | Licences et remerciements

Planète Casio est un site communautaire non affilié à Casio. Toute reproduction de Planète Casio, même partielle, est interdite.
Les programmes et autres publications présentes sur Planète Casio restent la propriété de leurs auteurs et peuvent être soumis à des licences ou copyrights.
CASIO est une marque déposée par CASIO Computer Co., Ltd