Question

Comment pouvez-vous mesurer le temps qu'une fonction prendra pour s'exécuter?

Il s’agit d’une fonction relativement courte et le temps d’exécution serait probablement de l’ordre de la milliseconde.

Cette question concerne un système embarqué, programmé en C ou C ++.

Était-ce utile?

La solution

Sur un système intégré, la meilleure façon de le faire est de définir une broche matérielle externe lorsque vous entrez dans la fonction et de l’effacer lorsque vous quittez la fonction. Ceci est fait de préférence avec une petite instruction de montage afin de ne pas biaiser trop vos résultats.

Modifier: L’un des avantages est que vous pouvez le faire dans votre application réelle et que vous n’avez pas besoin de code de test spécial. Les broches de débogage externes comme celles-ci sont (devraient être!) Une pratique courante pour chaque système embarqué.

Autres conseils

Il existe trois solutions possibles:

Solution matérielle :

Utilisez une broche de sortie libre sur le processeur et raccordez un oscilloscope ou un analyseur logique à la broche. Initialisez la broche sur un état bas, juste avant d’appeler la fonction que vous souhaitez mesurer, affirmez-la à un état haut et, juste après être revenue de la fonction, annulez-la.


    *io_pin = 1;
    myfunc();
    *io_pin = 0;

Solution Bookworm :

Si la fonction est relativement petite et que vous pouvez gérer le code désassemblé, vous pouvez ouvrir le fichier de données de l'architecture du processeur et compter les cycles nécessaires au processeur pour exécuter toutes les instructions. Cela vous donnera le nombre de cycles requis.
Time = # cycles * Fréquence d'horloge du processeur / ticks d'horloge par instructions

C’est plus facile à faire pour des fonctions plus petites, ou un code écrit en assembleur (pour un microcontrôleur PIC par exemple)

Solution de compteur d'horodatage :

Certains processeurs disposent d’un compteur d’horodatage qui s’incrémente à un rythme rapide (tous les quelques tics d’horloge du processeur). Il suffit de lire l'horodatage avant et après la fonction. Cela vous donnera le temps écoulé, mais sachez que vous devrez peut-être gérer le basculement de compteur.

Invoquez-le dans une boucle avec une tonne d'appels, puis divisez-le par le nombre d'appels pour obtenir le temps moyen.

alors:

// begin timing
for (int i = 0; i < 10000; i++) {
    invokeFunction();
}
// end time
// divide by 10000 to get actual time.

si vous utilisez Linux, vous pouvez chronométrer l'exécution d'un programme en tapant dans la ligne de commande:

time [funtion_name]

si vous n'exécutez que la fonction dans main () (en supposant que C ++), le reste du temps de l'application devrait être négligeable.

Je répète souvent l'appel (plusieurs millions) de fonctions, mais j'utilise également la méthode suivante pour réduire le temps système de la boucle:

start = getTicks();

repeat n times {
    myFunction();
    myFunction();
}

lap = getTicks();

repeat n times {
    myFunction();
}

finish = getTicks();

// overhead + function + function
elapsed1 = lap - start;

// overhead + function
elapsed2 = finish - lap;

// overhead + function + function - overhead - function = function
ntimes = elapsed1 - elapsed2;

once = ntimes / n; // Average time it took for one function call, sans loop overhead

Au lieu d'appeler function () deux fois dans la première boucle et une fois dans la deuxième boucle, vous pouvez simplement l'appeler une fois dans la première boucle et ne pas l'appeler du tout (c'est-à-dire une boucle vide) dans la seconde. La boucle vide pourrait être optimisée par le compilateur, ce qui vous donnerait des résultats de chronométrage négatifs:)

start_time = timer
function()
exec_time = timer - start_time

Windows XP / NT Embedded ou Windows CE / Mobile

Vous utilisez QueryPerformanceCounter () pour obtenir la valeur d’un compteur TRES RAPIDE avant et après votre fonction. Ensuite, vous soustrayez ces valeurs de 64 bits et obtenez un delta "ticks". En utilisant QueryPerformanceCounterFrequency (), vous pouvez convertir les " delta ticks " à une unité de temps réelle. Vous pouvez vous reporter à la documentation MSDN sur ces appels WIN32.

Autres systèmes intégrés

Sans système d'exploitation ou uniquement avec des systèmes d'exploitation de base, vous devrez:

  • programmez l’un des timers internes de la CPU pour qu’il fonctionne et compte librement.
  • configurez-le pour générer une interruption lorsque le temporisateur déborde, et incrémentez dans cette routine d'interruption un "report". variable (ceci vous permet de mesurer un temps plus long que la résolution du temporisateur choisi).
  • avant votre fonction, vous enregistrez le "carry" " valeur et la valeur du registre de la CPU contenant les graduations du compte à rebours que vous avez configuré.
  • idem après votre fonction
  • soustrayez-les pour obtenir un tick de compteur delta.
  • À partir de là, il ne vous reste plus qu'à savoir combien de temps une graduation signifie sur votre CPU / matériel, compte tenu de l'horloge externe et de la multiplication que vous avez configurées lors de la configuration de votre temporisateur. Vous multipliez cette "longueur de tick" " par les " delta ticks " vous venez de recevoir.

TRÈS IMPORTANT N'oubliez pas de désactiver et de restaurer les interruptions après avoir obtenu ces valeurs de minuterie (valeur retenue et valeur de registre), sinon vous risquez de sauvegarder des valeurs incorrectes.

NOTES

  • Ceci est très rapide, car il suffit de quelques instructions d'assemblage pour désactiver les interruptions, enregistrer deux valeurs entières et réactiver les interruptions. La soustraction et la conversion réelles en unités de temps réel ont lieu EN DEHORS de la zone de mesure du temps, c’est APRÈS votre fonction.
  • Vous voudrez peut-être insérer ce code dans une fonction pour le réutiliser de bout en bout, mais cela risque de ralentir un peu la cadence en raison de l'appel de la fonction et de l'enfoncement de tous les registres dans la pile, ainsi que des paramètres, puis de les réafficher. . Dans un système intégré, cela peut être important. Il serait peut-être préférable en C d'utiliser plutôt MACROS ou d'écrire votre propre routine d'assemblage en sauvegardant / restaurant uniquement les registres pertinents.

Dépend de votre plate-forme intégrée et du type de synchronisation que vous recherchez. Pour Linux embarqué, il existe plusieurs façons d’accomplir. Si vous souhaitez mesurer la quantité de temps processeur utilisée par votre fonction, vous pouvez procéder comme suit:

#include <time.h>
#include <stdio.h>
#include <stdlib.h>

#define SEC_TO_NSEC(s) ((s) * 1000 * 1000 * 1000)

int work_function(int c) {
    // do some work here
    int i, j;
    int foo = 0;
    for (i = 0; i < 1000; i++) {
        for (j = 0; j < 1000; j++) {
            for ^= i + j;
        }
    }
}

int main(int argc, char *argv[]) {
    struct timespec pre;
    struct timespec post;
    clock_gettime(CLOCK_THREAD_CPUTIME_ID, &pre);
    work_function(0);
    clock_gettime(CLOCK_THREAD_CPUTIME_ID, &post);

    printf("time %d\n",
        (SEC_TO_NSEC(post.tv_sec) + post.tv_nsec) -
        (SEC_TO_NSEC(pre.tv_sec) + pre.tv_nsec));
    return 0;
}

Vous devrez associer ceci à la bibliothèque temps réel, utilisez simplement le code suivant pour compiler votre code:

gcc -o test test.c -lrt

Vous pouvez également vouloir lire la page de manuel sur clock_gettime . L'exécution de ce code sur un système basé sur SMP peut entraîner des problèmes d'invalidation des tests. Vous pouvez utiliser quelque chose comme sched_setaffinity () ou la ligne de commande cpuset pour forcer le code sur un seul coeur.

Si vous souhaitez mesurer l'heure de l'utilisateur et celle du système, vous pouvez utiliser le fois (NULL) , qui renvoie un résultat en un tournemain. Ou vous pouvez modifier le paramètre pour clock_gettime () de CLOCK_THREAD_CPUTIME_ID en CLOCK_MONOTONIC ... mais faites attention à ne pas encombrer avec CLOCK_MONOTONIC .

Pour les autres plates-formes, vous êtes autonome.

Drew

J'implémente toujours une routine de ticker déclenchée par interruption. Ceci met ensuite à jour un compteur qui compte le nombre de millisecondes depuis le démarrage. Ce compteur est ensuite accessible avec une fonction GetTickCount ().

Exemple:

#define TICK_INTERVAL 1    // milliseconds between ticker interrupts
static unsigned long tickCounter;

interrupt ticker (void)  
{
    tickCounter += TICK_INTERVAL;
    ...
}

unsigned in GetTickCount(void)
{
    return tickCounter;
}

Dans votre code, vous devez le programmer comme suit:

int function(void)
{
    unsigned long time = GetTickCount();

    do something ...

    printf("Time is %ld", GetTickCount() - ticks);
}

Sous OS X (et probablement aussi sous Unix), utilisez "heure":

time python function.py

Si le code est .Net, utilisez la classe chronomètre (.net 2.0+) NOT DateTime.Now. DateTime.Now n'est pas mis à jour avec suffisamment de précision et vous donnera des résultats loufoques

Si vous recherchez une résolution inférieure à une milliseconde, essayez l’une de ces méthodes de minutage. Ils vous donneront tous la résolution au moins en dizaines ou centaines de microsecondes:

S'il s'agit de Linux intégré, regardez les temporisateurs Linux:

http://linux.die.net/man/3/clock_gettime

Java intégré, regardez nanoTime (), bien que je ne sois pas sûr que ce soit dans l'édition intégrée:

http: / /java.sun.com/j2se/1.5.0/docs/api/java/lang/System.html#nanoTime ()

Si vous souhaitez accéder aux compteurs de matériel, essayez PAPI:

http://icl.cs.utk.edu/papi/

Sinon, vous pouvez toujours aller à l'assembleur. Vous pouvez consulter la source PAPI de votre architecture si vous avez besoin d'aide pour cela.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top