• # setitimer

    Posté par . En réponse au message probleme pour faire un timer précis de l'ordre de la milliseconde. Évalué à 6.

    Salut,
    Je me suis tenté une implémentation avec setitimer...

    #define _POSIX_C_SOURCE 199309L
    #include <stdio.h>
    // #include <sys/timerfd.h>
     #include <errno.h>
     #include <string.h>
     #include <stdlib.h>
    // #include <unistd.h>
     #include <sys/mman.h>
    //#include <time.h>
    #include <unistd.h>
    #include <threads.h>
    #include <signal.h>
    #include <sys/time.h>
     #define COUNT 10001
     #define PERIODE 1000000 //1ms (in ns)
    mtx_t myLock;
    void (*old_handler)(int); // sighandler_t don't work !!! :-(
    void alarm_callback(int sig)
    {
     // Unlock du mutex pour continuer la boucle.
     (void) mtx_unlock(&myLock);
     if ((old_handler = signal(SIGALRM, alarm_callback)) == SIG_ERR)
     {
     fprintf(stderr,"Error setting SIGALARM in alarm_callback\n");
     return ;
     }
    }
     int main(int argc, char const *argv[])
     {
     if (mtx_init(&myLock, mtx_plain ) == thrd_error)
     {
     fprintf(stderr,"Error creating mutex\n");
     return -1;
     }
     // Lock one time to init locked. First loop must unlock
     if (mtx_lock(&myLock) == thrd_error)
     {
     fprintf(stderr,"Error locking mutex first time\n");
     return -1;
     }
     if ((old_handler = signal(SIGALRM, alarm_callback)) == SIG_ERR)
     {
     fprintf(stderr,"Error setting SIGALARM\n");
     return -1;
     }
     struct itimerval setTimer, oldTimer;
     setTimer.it_interval.tv_sec = 0;
     // Warning, c’est des us.
     setTimer.it_interval.tv_usec = PERIODE / 1000;
     // First period start in 1s
     setTimer.it_value.tv_sec = 1;
     setTimer.it_value.tv_usec = 0;
     // Timer values are defined by the following structures:
     //
     // struct itimerval {
     // struct timeval it_interval; /* Interval for periodic timer */
     // struct timeval it_value; /* Time until next expiration */
     // };
     //
     // struct timeval {
     // time_t tv_sec; /* seconds */
     // suseconds_t tv_usec; /* microseconds */
     // };
     if (setitimer(ITIMER_REAL,&setTimer, &oldTimer) == -1)
     {
     fprintf(stderr,"Setitimer initialization failed\n");
     return -1;
     }
     struct timespec now, previous;
     long somme = 0;
     long peak = PERIODE, base = PERIODE;
     long tabTimer[COUNT];
     __uint64_t ret;
     if (clock_gettime(CLOCK_MONOTONIC,&previous ) < 0 )
     {
     printf("error clock_gettime : %s\n", strerror(errno));
     return -1;
     }
     for (size_t i = 0; i < COUNT; i++)
     {
     // Wait for sync
     if (mtx_lock(&myLock) == thrd_error)
     {
     fprintf(stderr,"Error lock in loop number %d\n",i);
     return -1;
     }
     clock_gettime(CLOCK_MONOTONIC,&now );
     tabTimer[i] = (now.tv_sec - previous.tv_sec) * 1000000000 + now.tv_nsec - previous.tv_nsec;
     previous = now;
     }
     const long high_10 = PERIODE + PERIODE / 10;
     const long low_10 = PERIODE - PERIODE / 10;
     int higher = 0;
     int lower = 0;
     int normal = 0;
     //we write results
     for (size_t i = 1; i < COUNT; i++)
     {
     // printf("[%2d] = %ld\n",i,tabTimer[i]);
     somme += tabTimer[i];
     if( tabTimer[i] > peak)
     {
     peak = tabTimer[i];
     }
     if( tabTimer[i] < base)
     {
     base = tabTimer[i];
     }
     if (tabTimer[i] > high_10)
     {
     higher++;
     }
     else if (tabTimer[i] < low_10)
     {
     lower++;
     }
     else
     {
     normal++;
     }
     }
     int total = lower + higher + normal;
     printf("average en ns = %ld, peak = %ld(+%2.2f%%), base = %ld(-%2.2f%%)\n", somme/(COUNT - 1), peak, (((float)peak/PERIODE) - 1) * 100, base, (1. - ((float)base)/PERIODE)*100);
     printf(" Total : %d,\n"
     "\t- lower : %6d (%2.2f%%)\n"
     "\t- normal: %6d (%2.2f%%)\n"
     "\t- higher: %6d (%2.2f%%)\n",
     total,
     lower, 100. * ((float) lower)/total,
     normal, 100.* ((float)normal)/total,
     higher, 100.* ((float)higher)/total);
     return 0;
     }

    Pour le sigalarm, il faut le recabler à chaque fois.
    Voici quelques stats, j’y ai ajouté le nombre de fois ou on a un écart de plus de 10%. De plus, si on déborde sur un cycle, la différence entre les deux sera forcément très inférieur sur le cycle suivant. D’où la relative symétrie (+ou- 1) entre les higher et les lower.

    $ ./a.out 
    average en ns = 999998, peak = 1057467(+5.75%), base = 944193(-5.58%)
     Total : 10000,
     - lower : 0 (0.00%)
     - normal: 10000 (100.00%)
     - higher: 0 (0.00%)
    $ ./a.out 
    average en ns = 1000098, peak = 2083044(+108.30%), base = 603560(-39.64%)
     Total : 10000,
     - lower : 1 (0.01%)
     - normal: 9997 (99.97%)
     - higher: 2 (0.02%)
    $ ./a.out 
    average en ns = 999998, peak = 1206658(+20.67%), base = 796835(-20.32%)
     Total : 10000,
     - lower : 2 (0.02%)
     - normal: 9997 (99.97%)
     - higher: 1 (0.01%)
    $ ./a.out 
    average en ns = 999997, peak = 1107952(+10.80%), base = 894580(-10.54%)
     Total : 10000,
     - lower : 1 (0.01%)
     - normal: 9998 (99.98%)
     - higher: 1 (0.01%)
    $ su 
    Mot de passe : 
    ####################
    # En Temps Réel
    ####################
    .../Linuxfr/TR# chrt 50 ./a.out 
    average en ns = 999997, peak = 1009857(+0.99%), base = 978338(-2.17%)
     Total : 10000,
     - lower : 0 (0.00%)
     - normal: 10000 (100.00%)
     - higher: 0 (0.00%)
    .../Linuxfr/TR# chrt 50 ./a.out 
    average en ns = 999997, peak = 1011019(+1.10%), base = 979650(-2.03%)
     Total : 10000,
     - lower : 0 (0.00%)
     - normal: 10000 (100.00%)
     - higher: 0 (0.00%)
    .../Linuxfr/TR# chrt 50 ./a.out 
    average en ns = 999997, peak = 1009547(+0.95%), base = 976955(-2.30%)
     Total : 10000,
     - lower : 0 (0.00%)
     - normal: 10000 (100.00%)
     - higher: 0 (0.00%)
    .../Linuxfr/TR# chrt 50 ./a.out 
    average en ns = 999997, peak = 1015868(+1.59%), base = 979951(-2.00%)
     Total : 10000,
     - lower : 0 (0.00%)
     - normal: 10000 (100.00%)
     - higher: 0 (0.00%)
    .../SSD/C++/Linuxfr/TR#

    Un essai supplémentaire sur 2 minutes (2 ×ばつかける 60 ×ばつかける 1000 = 120 000ms)

    $ ./a.out 
    average en ns = 999999, peak = 1425844(+42.58%), base = 585376(-41.46%)
     Total : 120000,
     - lower : 22 (0.02%)
     - normal: 119955 (99.96%)
     - higher: 23 (0.02%)
    $ su 
    Mot de passe : 
    .../Linuxfr/TR# chrt 50 ./a.out 
    average en ns = 999999, peak = 1030337(+3.03%), base = 968720(-3.13%)
     Total : 120000,
     - lower : 0 (0.00%)
     - normal: 120000 (100.00%)
     - higher: 0 (0.00%)
    # Ajout d'une affinité CPU
    .../Linuxfr/TR# taskset 1 chrt 50 ./a.out 
    average en ns = 999999, peak = 1272574(+27.26%), base = 727705(-27.23%)
     Total : 120000,
     - lower : 10 (0.01%)
     - normal: 119981 (99.98%)
     - higher: 9 (0.01%)
    .../C++/Linuxfr/TR#

    En forçant l’affinité sur un CPU, on augmente l’erreur... ça ma surpris. Il faudrait voir si on peut interdire un CPU à tout le monde sauf un processus ;-)
    Après, j'avais choisi le CPU 0, peut-être que en prenant le 3...

    Linuxfr/TR# taskset 4 chrt 50 ./a.out 
    average en ns = 999999, peak = 1018414(+1.84%), base = 971595(-2.84%)
     Total : 120000,
     - lower : 0 (0.00%)
     - normal: 120000 (100.00%)
     - higher: 0 (0.00%)
    .../Linuxfr/TR#

    C’est mieux... est-ce le hazard, ou les IT sont cablé sur le premier CPU sous Linux ?

    Voilà pour mes tests du soir.

    Bonne nuit