Quelle est la précision des timers d’intervalle sous Linux?

J’essaie de caractériser la gigue temporisée sous Linux. Ma tâche consistait à exécuter des timers de 100 ms et à voir comment les chiffres fonctionnaient.

Je travaille sur une machine multicœur. J’ai utilisé un programme utilisateur standard avec setitimer (), le même programme d’exécution que root, puis avec une affinité de processeur, et enfin avec une affinité de processeur et une priorité de processus. Ensuite, j’ai exécuté la même chose avec le kernel PREEMPT_RT, puis j’ai exécuté les exemples en utilisant clock_nanosleep () comme dans le code de démonstration de la page PREEMPT_RT. De toutes les courses, les performances de la timer étaient très similaires, sans réelle différence malgré les changements.

Notre objective final est une timer stable. Le meilleur cas que je pouvais recevoir régulièrement était d’environ 200us. L’histogramme pour tous les cas montre un comportement vraiment étrange. D’une part, je ne m’attendrais pas à ce que les minuteurs déclenchent tôt. Mais ils le font. Et comme vous pouvez le voir sur l’histogramme, j’obtiens des creux de part et d’autre du décalage. Ceux-ci sont visibles dans trois bandes dans le deuxième graphique. Dans le premier graphique, l’axe X est en microsecondes. Dans le deuxième graphique, l’axe Y est en microsecondes.

J’ai effectué un test de 30 secondes (soit 300 événements de timer) 100 fois pour générer des nombres. Vous pouvez les voir dans les schémas suivants. Il y a une grosse baisse à 200us. Tous les décalages d’horloge des événements de la timer 30000 sont représentés graphiquement dans le deuxième graphique, où vous pouvez voir des valeurs aberrantes.

L'axe X est en microsecondesL'axe Y est en microsecondes

Donc, la question est la suivante: est-ce que quelqu’un d’autre a déjà fait ce genre d’parsing auparavant? Avez-vous vu le même genre de comportement? Mon hypothèse est que le kernel RT aide sur les systèmes avec de lourdes charges, mais dans notre cas, cela n’a pas aidé à éliminer la gigue temporelle. Est-ce votre expérience?

Voici le code. Comme je l’ai déjà dit, j’ai modifié l’exemple de code sur le site PREEMPT_RT qui utilise la fonction clock_nanosleep (), je n’inclurai donc pas mes modifications minimales pour cela.

#include  #include  #include  #include  #include  #define US_PER_SEC 1000000 #define WAIT_TIME 100000 #define MAX_COUNTER 300 int counter = 0; long long last_time = 0; static long long times[MAX_COUNTER]; int i = 0; struct sigaction sa; void timer_handler(int signum) { if (counter > MAX_COUNTER) { sigaction(SIGALRM, &sa, NULL); for (i = 0; i < MAX_COUNTER; i++) { printf("%ld\n", times[i]); } exit(EXIT_SUCCESS); } struct timeval t; gettimeofday(&t, NULL); long long elapsed = (t.tv_sec * US_PER_SEC + t.tv_usec); if (last_time != 0) { times[counter] = elapsed - last_time; ++counter; } last_time = elapsed; } int main() { struct itimerval timer; memset(&sa, 0, sizeof(sa)); sa.sa_handler = &timer_handler; sigaction(SIGALRM, &sa, NULL); timer.it_value.tv_sec = 0; timer.it_value.tv_usec = 1; timer.it_interval.tv_sec = 0; timer.it_interval.tv_usec = WAIT_TIME; setitimer(ITIMER_REAL, &timer, NULL); while (1) { sleep(1); } } 

EDIT: ceci est sur un Xeon E31220L, fonctionnant à 2,2 GHz, fonctionnant sous Fedora Core 19 x86_64.

Vous avez raison de ne pas vous attendre à ce que les minuteurs mettent le feu tôt – et ils ne le font pas. Le déclenchement anticipé apparent est dû au fait que vous ne mesurez pas le temps écoulé depuis l’expiration du compte à rebours précédent – vous gettimeofday() le temps écoulé depuis l’appel gettimeofday() précédent. S’il y avait un délai entre l’expiration du temporisateur et le processus réellement planifié, vous verrez ce gettimeofday() en retard et le prochain s’exécutant de la même manière .

Au lieu de gettimeofday() la différence entre les gettimeofday() , essayez de gettimeofday() les temps absolus retournés, puis comparez les temps renvoyés à N * 100ms après l’heure initiale.

Si vous souhaitez que PREEMPT_RT vous aide, vous devez définir une stratégie de planificateur en temps réel pour votre programme de test ( SCHED_FIFO ou SCHED_RR ), qui requirejs une racine.