bonjour à tous,
je souhaite faire un timer via timerfd_create qui reveille mon programme toutes les 1 milliseconde (ms). j'aimerais une erreur que de 10% soit pas plus de 1.1ms. Mon programme à un timer moyen de 1ms (super :) ) mais a parfois des piques à 1,2 ms voir 1,4ms :(
Je l'ai passé en fifo priorité 99, j'ai viré toutes les interruptions venant de mon cpu8 afin d'empecher que mon processus soit interrompu (via /proc/irq/smp_affinity), j'ai bien entendu forcé mon programme via la commande taskset a etre que sur le cpu8, j'ai mis tous mes cpus en mode performance, j'ai mis la variable /proc/sys/kernel/sched_rt_runtime_us = -1
et pourtant rien à faire j'ai toujours des piques avec une erreur pouvant aller jusqu'a plus de 20%.
Je demande des idées, merci d'avance :)
PS : je précise avoir un bon PC donc il devrait tenir la charge
Je vous montre mon code ca pourra peut etre aidé:
#include <stdio.h>
#include <sys/timerfd.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#define COUNT 10001
#define PERIODE 1000000 //1ms
int main(int argc, char const *argv[])
{
int timerfd;
if( (timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC )) < 0)
{
printf("error timerfd : %s\n", strerror(errno));
return -1;
}
struct timespec initTimer;
if( clock_gettime(CLOCK_MONOTONIC, &initTimer ) < 0 )
{
printf("error clock_gettime : %s\n", strerror(errno));
return -1;
}
struct itimerspec setTimer ;
setTimer.it_interval.tv_sec = 0;
setTimer.it_interval.tv_nsec = PERIODE;
setTimer.it_value.tv_sec = initTimer.tv_sec ;
setTimer.it_value.tv_nsec = 0;
if( timerfd_settime(timerfd, TFD_TIMER_ABSTIME, &setTimer, NULL ) < 0)
{
printf("error timerfd_settime : %s\n", strerror(errno));
return -1;
}
struct timespec now, previous;
long somme = 0;
long peak = PERIODE, base = PERIODE;
long tabTimer[COUNT];
__uint64_t ret;
if( mlockall( MCL_CURRENT | MCL_FUTURE ) < 0)
{
printf("error mlockall : %s\n", strerror(errno));
return -1;
}
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++)
{
read( timerfd, &ret, sizeof(__uint64_t));
clock_gettime(CLOCK_MONOTONIC, &now );
tabTimer[i] = (now.tv_sec - previous.tv_sec) * 1000000000 + now.tv_nsec - previous.tv_nsec;
previous = now;
}
//we write results
for (size_t i = 1; i < COUNT; i++)
{
somme += tabTimer[i];
if( tabTimer[i] > peak)
{
peak = tabTimer[i];
}
if( tabTimer[i] < base)
{
base = tabTimer[i];
}
}
printf("average en ns = %ld, peak = %ld, base = %ld\n", somme/(COUNT - 1), peak, base);
return 0;
}
# mauvaise hardware, mauvais os ?
Posté par totof2000 . Évalué à 5. Dernière modification le 26 janvier 2022 à 22:21.
Pour obtenir ce que tu veux, il te faudrait peut etre te tourner vers une extensio real time pour linux (rtlinux, xenomai, … ), avec peut etre un hardware mieux adapté ?
Ou alors partir vers un is dédié au temps réel.
C'est pour quoi faire ?
# Vérifie que tu es vraiment en priorité haute
Posté par cg . Évalué à 4. Dernière modification le 26 janvier 2022 à 23:13.
Sans jouer avec les affinités ni rien :
J'avais conclu que c'est parce que je suis dans le groupe
audio
qui a le droit de faire du RT (cf/etc/security/limits.conf
), mais ça fonctionne avec un autre user qui n'y est pas :-/.Tu peux aussi vérifier la prio avec /proc/PID/limits :
Je suis d'accord avec totof que si tu veux aller plus loin que jouer un peu avec les timers, il faudra sans doute un kernel spécifique, voire du matériel. Bon, la ms c'est pas trop demander non plus ;).
(moi là c'est sur un vieux laptop)
[^] # Re: Vérifie que tu es vraiment en priorité haute
Posté par totof2000 . Évalué à 3.
Le problème c'est pas la ms, mais la marge d'erreur qui peut être assez fluctuante sur un noyau Linux, et sur un CPU de type x86.
Il y a probablement des choses à tuner côté configuration noyau avant de passer par des modules spécifiques temps réel, mais tout ça dépend du besoin, d'ou ma question.
# J'ai été confronté au même problème.
Posté par foobarbazz . Évalué à 2.
La solution que j'avais trouvé c'était le le sleep pendant le temps qu'il faut - une marge de sécurité + l'attente active pour les derniers instant.
Il me semble que j'avais mis en place un truc pour ajuster le temps d'attente active.
Sinon, comme alternative entre sleep et l'attente active il y a yield, je ne sais pas ce que ça donne.
[^] # Re: J'ai été confronté au même problème.
Posté par ChocolatineFlying . Évalué à 1.
une interruption ne serais pas plus efficace, mais ce n'est plus en mode user
# Set affinity ?
Posté par Anthony Jaguenaud . Évalué à 3.
Hello,
Tu n’indiques pas comment tu lances ton programme.
En mode temps réel sur un noyau standard, la seule chose plus prioritaire, c’est le noyau.
Une solution pourrait-être de forcer ton processus sur un cœur, et d’interdire ce cœur au noyau.
Sinon, le réveil un peu plus tôt puis boucle active peut-être viable, et vraiment précis.
# Paramètre de timerfd_create
Posté par Anthony Jaguenaud . Évalué à 4.
Re,
Dans le
man timerfd_create
,il y a ce passage :
As-tu essayé avec REALTIME ?
[^] # Re: Paramètre de timerfd_create
Posté par Anthony Jaguenaud . Évalué à 2.
Le
get_clocktime
aussi a le paramètreCLOCK_REALTIME
.Sinon, tu peux utiliser la fonction
setitimer
. Par contre, c'est un peu plus complexe, il faut intercepter le signalSIGALARM
, dans la fonction callback, tu libères un sémaphore (mutex). Et dans ton code, tu bloques le sémaphores.Autre chose, le temps réel, ça veut dire arrivé à temps… donc si malgré les erreurs tu arrives à finir ton algo avant la deadline, pourquoi chercher à réagir plus vite ? Néanmoins j’avoue que l’écart type d’erreur laisse songeur. ;-)
# driver
Posté par Nicolas Boulay (site web personnel) . Évalué à 3.
Tu peux avoir des drivers mauvais pour le rt (proprio) etc…
Tu peux tenter de les désactiver.
"La première sécurité est la liberté"
# setitimer
Posté par Anthony Jaguenaud . Évalué à 6.
Salut,
Je me suis tenté une implémentation avec setitimer…
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.
Un essai supplémentaire sur 2 minutes (2 × 60 × 1000 = 120 000ms)
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…
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
# désactiver l'hyperthreading ?
Posté par YvanM (site web personnel) . Évalué à 3.
Je ne suis pas certain que ce soit pertinent dans ce cas, mais une des recommandations que j'avais lue pour faire du temps réel sous Linux était de désactiver l'hyperthreading. Si jamais, voilà un script que j'avais trouvé pour le faire depuis Linux.
# le probleme vient peut etre que mes cpu entre dans un etat de sommeil
Posté par cosmoff . Évalué à 1. Dernière modification le 29 janvier 2022 à 13:41.
merci pour vos messages,
-> je lance mon programme avec sudo chrt -f 99 ./a.out
-> je ne pense pas qu'il soit necessaire de passer sur du PREMPT_RT ou xenomai pour avoir un timer de 1ms avec une marge d'erreur seulement de 10%
-> pour moi le fait d'utiliser timerfd_create ou timer_create ou setitimer revient au meme.
j'ai remarqué que j'avais une erreur seulement de 2 à 3% (au lieu des 10 à 40%) quand mon systeme était stressé (avec le programme stress). On me renseignant un peu j'ai appris que le systeme pouvait entrer dans des niveaux de sommeil (suspend, hibernation), et lorsque l'interruption de mon timer se déclenche ca met un certain temps avant de pouvoir déclencher le handler associé. J'ai l'impression que le probleme vient de là.
Je vais donc essayer de changer certain parametre noyau. Peut etre retiré le mode tickless, mettre une frequence de timer à 1000Hz (au lieu des 250Hz par défaut) et essayer de retirer les états de sommeil de mes cpu
[^] # Re: le probleme vient peut etre que mes cpu entre dans un etat de sommeil
Posté par NeoX . Évalué à 3.
commence peut-etre par désactiver les états de sommeil des CPUs avant de vouloir changer des paramètres de noyau, de frequence de timer…
# Revenir à pourquoi une telle contrainte.
Posté par dalfab . Évalué à 4.
Bonjour,
Il faudrait peut-être revenir à pourquoi tu as ce besoin de timing ultra précis.
Si juste avant l'échéance une interruption réseau ou disque déclenche c'est une centaine de µs de perdues. Si on a les 2 et qu'en plus le processeur doit sortir de veille c'est 300µs d'écart. C'est inévitable quelle que soit la priorité de ton application. Je travaille sur des noyaux temps réel et je descend au mieux à la milliseconde d'incertitude et toi tu souhaites quelques dizaines de µs de fluctuation et en plus sous Linux.
Sur une carte dédiée, on peut avoir une contrainte beaucoup plus forte. Pour reprendre mon cas, je dois récupérer des événements toutes les secondes et utiliser leur durée. J'ai une contrainte de 0.0000015ms sur la mesure de cette durée. Aucun problème la durée est garantie et est lue toutes les secondes environ.
Donc si tu as besoin d'un contrainte aussi forte, il faut peut-être revenir au pourquoi et trouver un moyen de la garantir. Et il faut toujours se poser la question quelles sont les conséquences si la contrainte n'est pas atteinte en séparant les cas : écart maximum fréquent et écart maximum exceptionnel.
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.