Des résultats étranges lors de la mesure du temps delta sous Linux

Mise à jour: calculs delta fixes dans le code, le problème persiste

Les gens, pourriez-vous s’il vous plaît expliquer pourquoi je reçois des résultats très étranges de temps en temps en utilisant le code suivant:

#include  #include  #include  int main() { struct timeval start, end; long mtime1, mtime2, diff; while(1) { gettimeofday(&start, NULL); usleep(2000); gettimeofday(&end, NULL); mtime1 = (start.tv_sec * 1000 + start.tv_usec/1000.0); mtime2 = (end.tv_sec * 1000 + end.tv_usec/1000.0); diff = mtime2 - mtime1; if(diff > 10) printf("WTF: %ld\n", diff); } return 0; } 

(Vous pouvez le comstackr et l’exécuter avec: gcc test.c -o out -lrt && ./out )

Ce que je vis, ce sont des grandes valeurs sporadiques de diff variable presque toutes les secondes ou même plus souvent, par exemple:

 $ gcc test.c -o out -lrt && ./out WTF: 14 WTF: 11 WTF: 11 WTF: 11 WTF: 14 WTF: 13 WTF: 13 WTF: 11 WTF: 16 

Comment cela est-il possible? Est-ce l’OS à blâmer? Fait-il trop de changement de contexte? Mais ma boîte est inactive (charge moyenne: 0.02, 0.02, 0.3).

Voici la version de mon kernel Linux:

 $ uname -a Linux kurluka 2.6.31-21-generic #59-Ubuntu SMP Wed Mar 24 07:28:56 UTC 2010 i686 GNU/Linux 

Les fonctions de veille permettent uniquement de dormir AU MOINS un certain temps. Comme Linux n’est pas un système d’exploitation temps réel, vous ne pouvez PAS être sûr qu’il dormira SEULEMENT le temps que vous souhaitez. C’est un problème car vous ne pouvez pas compter sur cette valeur. Comme vous l’avez souligné, il arrive que le temps de sumil soit vraiment énorme.

Le planificateur Linux ne peut pas garantir cela. Avec un OS en temps réel, vous pouvez l’obtenir.

Votre formule est fausse dans un sens, mais je pense que cela ne peut pas être la raison pour laquelle vous avez un temps de sumil aussi long. Je vérifie les deux formules avec cet extrait, et j’ai le même résultat:

 #include  #include  #include  #include  int main() { struct timeval start, end; long mtime, mtime2, start_time, end_time, seconds, useconds; while(1) { gettimeofday(&start, NULL); usleep(2000); gettimeofday(&end, NULL); seconds = end.tv_sec - start.tv_sec; useconds = end.tv_usec - start.tv_usec; mtime = ((seconds) * 1000 + useconds/1000.0) + 0.5; start_time = ((start.tv_sec) * 1000 + start.tv_usec/1000.0) + 0.5; end_time = ((end.tv_sec) * 1000 + end.tv_usec/1000.0) + 0.5; mtime2 = end_time - start_time; if(mtime > 10 || mtime2 > 10) { printf("WTF: %ld\n", mtime); printf("WTF2: %ld\n", mtime2); } } return 0; } 

Les resultats :

 $ gcc test.c -o out -lrt && ./out WTF: 11 WTF2: 12 WTF: 21 WTF2: 21 

Je pensais que c’était faux, car la partie useconds est cyclique et pourrait conduire à une grosse différence négative. Mais cela ne vous conduira pas à utiliser autant d’entiers longs signés …

mes2cents

Edit : de l’homme nanosleep:

La mise en œuvre actuelle de nanosleep () est basée sur le mécanisme de timer du kernel normal, qui a une résolution de 1 / HZ s (voir time (7)). Par conséquent, nanosleep () s’interrompt toujours pendant au moins la durée spécifiée. Toutefois, le processus peut être prolongé jusqu’à 10 ms par rapport à la durée spécifiée. Pour la même raison, la valeur renvoyée dans le cas d’un signal délivré dans * rem est généralement arrondie au multiple de 1 / HZ supérieur.

Je suppose que cela est lié au système d’exploitation. Essayez d’exécuter le processus à la priorité temps réel (voir le programme chrt) et voyez si cela vous aide.

Sur une autre note, vous calculez correctement mtime. Voici une routine que j’utilise, bien que ce soit pour struct timespec plutôt que pour struct timeval (nanosecondes au lieu de microsecondes), le principe doit être clair:

 timespec diff(timespec start, timespec end) { timespec temp; if ((end.tv_nsec - start.tv_nsec) < 0) { temp.tv_sec = end.tv_sec - start.tv_sec - 1; temp.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec; } else { temp.tv_sec = end.tv_sec - start.tv_sec; temp.tv_nsec = end.tv_nsec - start.tv_nsec; } return temp; } 

Trouvé, voir la page de manuel

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

par la granularité des timers du système.

ce qui est à 10 ms. Ainsi, la usleep peut expirer bien avant que le processus ne soit reprogrammé.

Cela correspond également aux valeurs que vous obtenez, qui sont dans l’ordre de grandeur d’une tranche de temps «normale».

J’ai déjà pris de telles mesures et ma conclusion est exactement la même. Cela se passe également sous Windows et Linux.

Un programme créant un histogramme en 10 ^ -n de secondes donne les résultats suivants.

 0.1 0 0.01 0 0.001 0 0.0001 2 1e-05 24 1e-06 69726023 Total: 69726049 Duration: 6.47403 seconds. Average: 0.0928495 microseconds. 

Mais notez qu’il s’agit d’un tout nouveau système. Je me souviens de l’avoir utilisé il ya un an sur un système de 2004 et d’avoir enregistré quelques coups par seconde de 0,01 (plus de 10 ms).

Votre formule est fausse. Vous devez convertir les deux fois dans la même échelle. Dans votre exemple, ms.

 double mtime1 = (start.tv_sec * 1000 + start.tv_usec/1000.0) ; double mtime2 = (end.tv_sec * 1000 + end.tv_usec/1000.0) ; double diff = mtime2 - mtime1; if(diff > 10) printf("WTF: %ld\n", diff); 

Vous devez soustraire les valeurs corrigées

Exemple: t1 = 1.999999 t2 = 2.000001 donc un intervalle de 2 µs

Avec votre formule, vous calculez:

2 - 1 == 1 et 1 - 9999999 donnant un résultat de (1 * 1000 - 999998 / 1000) + 0.5 == 0.502, ce qui est évidemment faux.

Ma méthode donne:

 mtime1 = (1 * 1000 + 999999 / 1000) = 1999.999 mtime2 = (2 * 1000 + 1 / 1000) = 2000.001 2000.001 - 1999.999 = 0.002 ms