11
votes

Pourquoi le changement de la latence de réseau mesuré-il si j'utilise un sommeil?

J'essaie de déterminer le temps qu'il faut pour une machine pour recevoir un paquet, le traiter et redonner une réponse.

Cette machine, que je vais appeler "serveur", exécute un programme très simple. , qui reçoit un paquet ( recv (2) ) dans un tampon, copie le contenu reçu ( memcpy (3) ) sur un autre tampon et envoie le paquet ( Envoyer (2) ). Le serveur exécute NetBSD 5.1.2.

Mon client mesure le temps de déplacement à plusieurs reprises ( pkt_count ): xxx

J'ai supprimé des contrôles d'erreur et d'autres choses mineures pour la clarté. Le client fonctionne sur un Ubuntu 12.04 64 bits. Les deux programmes fonctionnent dans la priorité en temps réel, bien que seul le noyau Ubuntu soit en temps réel (-RT). La connexion entre les programmes est TCP. Cela fonctionne bien et me donne une moyenne de 750 microsecondes.

Toutefois, si j'accepte l'appel de Nanosleep commenté (avec un sommeil de 100 Â Â), mes mesures ont chuté de 100 Â, donnant une moyenne de 650 Â. Si je dors pendant 200 Â, les mesures baissent à 550 Â, etc. Cela monte jusqu'à ce qu'un sommeil de 600 Â, donnant une moyenne de 150 Â. Ensuite, si j'augmente le sommeil à 700 Â, mes mesures passent jusqu'à 800 Â Â en moyenne. J'ai confirmé les mesures de mon programme avec Wireshark.

Je ne peux pas comprendre ce qui se passe. J'ai déjà défini l'option TCP_Nodelay Socket dans le client et le serveur, aucune différence. J'ai utilisé UDP, aucune différence (même comportement). Je suppose donc que ce comportement n'est pas dû à l'algorithme Nagle. Que pourrait-il être?

[update]

Voici une capture d'écran de la sortie du client avec Wireshark. Maintenant, j'ai dirigé mon serveur dans une autre machine. J'ai utilisé le même système d'exploitation avec la même configuration (car il s'agit d'un système en direct dans un lecteur de stylo), mais le matériel est différent. Ce comportement ne s'est pas présenté, tout a fonctionné comme prévu. Mais la question reste: pourquoi cela se produit-il dans le matériel précédent?

Comparaison de sortie < / p>

[Mise à jour 2: Plus d'infos]

Comme je l'ai déjà dit, j'ai testé ma paire de programmes (client / serveur) dans deux ordinateurs de serveurs différents. J'ai tracé les deux résultats obtenus.

Comparaison entre deux serveurs

Le premier serveur (le bizarre) est un Ordinateur de carte unique RTD , avec une interface Ethernet 1GBPS. Le deuxième serveur (la normale) est un ordinateur de carte unique de diamant avec une interface Ethernet de 100 Mbps. Les deux gèrent le même système d'exploitation (Netbsd 5.1.2) du même Pendrive.

de ces résultats, je pense que ce comportement est dû soit au conducteur, soit à la nic elle-même, bien que je reste Imaginez pourquoi cela se produit ...


12 commentaires

Lorsque vous dites que vous avez "confirmé les mesures de votre programme avec Wireshark", pouvez-vous clarifier exactement ce que vous avez fait pour vérifier? Je m'excuse si cela semble sceptique, mais il n'ya absolument aucune raison que mon cerveau simple puisse penser que l'appel de Nanosleep devrait réellement affecter le RTT des paquets. Après l'après, il vient après votre appel de Recv ... Je pense que c'est plus probablement un problème avec le code que vous utilisez pour mesurer le RTT. Ma théorie, cependant, sort de la fenêtre si Wireshark vérifie effectivement les résultats que vous voyez.


Je gère le programme pour mille itérations pendant que Wirehark est en cours d'exécution. Une fois que le programme a terminé, je regarde dans le journal Wireshark et comparez les mesures avec celles imprimées par mon programme. Ils sont très similaires. Je pouvais aussi attacher une capture d'écran ... Cela me rend sur Noix, car je ne peux pas penser honnêtement à une raison ... peut-être que le problème est sur le serveur?


Souhaitez-vous afficher une capture d'écran? J'ai une théorie ..


J'ai ajouté une capture d'écran et plus d'informations.


Quelle est la valeur nanosleep définie pour la capture d'écran que vous avez envoyée?


Je viens de lire votre mise à jour plus attentivement. C'est un comportement totalement bizarre. Je peux seulement suggérer qu'il existe des paramètres de matériel bizarre sur votre carte réseau. Si vous travaillez dans les mathématiques qui décrivent le comportement survenant, c'est comme si votre NIC ait une minuterie de retard qui commence chaque fois qu'un paquet est reçu et ne traitera pas le prochain paquet avant l'expiration de la minuterie.


J'ai juste eu une pensée ... Quel est le premier RTT qui est mesuré lorsque vous exécutez votre client avec le serveur d'origine?


Vraisemblablement. Je ne peux pas obtenir le matériel précédent pour démarrer, j'avais ce problème intermittent et je l'ai de nouveau. Si je commence à démarrer, je posterai ici. Je reçois votre point, si la première mesure est raisonnable, il est probable que cette minuterie dans la carte réseau (ou dans le conducteur) existe, ce qui est, en effet, bizarre ...


Avez-vous le même résultat si vous retirez les impressions de la boucle? (stockez les résultats dans un tableau ou regardez sur WireShark). Courir à la priorité en temps réel change de nombreuses choses lorsque vous attrapez le processeur et ne laissez pas le système d'exploitation faire un peu de ménage. par exemple. avec Nanosleep, vous laissez la capture du système d'exploitation lorsque vous ne mesurez pas, sans nanosleep, il vous entraîne lorsque vous mesurez, peut-être après avoir copié le paquet sur la pile de réseau - ce qui pourrait ne pas le placer sur le fil immédiatement s'il y a d'autres choses à faire. (Priorité en temps réel ne s'étend pas de l'espace utilisateur au fil de réseautage)


J'ai ajouté plus d'informations. Je ne crois pas que le problème est dans le client, car le système d'exploitation devrait être en mesure d'exécuter ce ménage dans le RECV appel ou dans le impression de caractères appel. Tous deux transfèrent le contrôle au système d'exploitation.


@bsmartins Pouvez-vous désactiver la préemption lorsque vous mesurez le temps autour de votre section Send / Recv? ... Prêp_Disable () / Prêp_Enable () fonctionne?


Si j'ai bien compris, c'est l'intervalle entre les paquets qui affecte le temps de trajet aller-retour. Une possibilité de tels effets est que si la charge sur la machine bizarre est si faible, le gouverneur de la CPU continue de changer de fréquence de la CPU. C'est plus probablement une question de pilote, cependant.


5 Réponses :


0
votes

Ceci est un (je l'espère éduquée) deviner, mais je pense que cela pourrait expliquer ce que vous voyez.

Je ne sais pas combien de temps réel du noyau linux est. Il pourrait ne pas être totalement préventive ... Donc, avec cette mise en garde, continue:) ... p>

Selon le planificateur, une tâche sera peut-être ce qu'on appelle un « quanta », ce qui est Juste un peu de temps, il peut fonctionner avant avant qu'une autre tâche de même priorité ne soit programmée. Si le noyau n'est pas complètement préventif, cela pourrait également être le point où une tâche de priorité plus élevée peut fonctionner. Cela dépend des détails du planificateur que je ne connais pas assez. P>

Partout entre votre premier et deuxième gettime gettime votre tâche peut être préempté. Cela signifie simplement que c'est "pause" et une autre tâche permet d'utiliser la CPU pendant une certaine quantité de temps. P>

La boucle sans le sommeil pourrait aller quelque chose comme ça p> xxx

Lorsque vous mettez le sommeil Nanosecond dans, il s'agit probablement d'un point sur lequel le planificateur est capable de fonctionner avant l'expiration de la tâche actuelle (la même chose s'appliquerait à RECV () aussi, ce qui bloque). Alors peut-être ce que vous obtenez est quelque chose comme ceci p>

clock_gettime(CLOCK_MONOTONIC, &start);        
send(sock, send_buf, pkt_size, 0);
recv(sock, recv_buf, pkt_size, 0);
clock_gettime(CLOCK_MONOTONIC, &end);  

struct timespec nsleep = {.tv_sec = 0, .tv_nsec = 100000};
nanosleep(&nsleep, NULL);

<----- PREMPTION .. nanosleep allows the scheduler to kick in because this is a pre-emption point
       ... another task runs for a little while     
<----- PREMPTION again and your back on the CPU

// Now it so happens that because your task got prempted where it did, the time
// measurement has not been artifically increased. Your task then can fiish the rest of 
// it's quanta
printf("%.3f ", timespec_diff_usec(&end, &start));

clock_gettime(CLOCK_MONOTONIC, &start);        
... and so on


4 commentaires

J'ai ajouté plus d'informations. Je crois que mon ordinateur client a une RTC (c'est un ordinateur I7 HP après tout). Ceci, avec l'utilisation d'un noyau en temps réel (qui a été configuré avec config_preempt_rt_full) devrait faire une échelle de centaines de centaines ou même de dizaines de nanosecondes réalisables. Je ne crois pas que ceci est le résultat d'une préemption entre les deux clock_gettime , car cela arrive systématiquement (c'est-à-dire dans chaque itération) ...


@bsmartins d'accord. Malheureusement, c'était ma seule idée :) Si vous trouvez la solution, veuillez le poster ... il serait intéressant de voir ce qui l'a causé.


Le noyau Linux ne sera pas capable de le faire avec précision. Votre processus peut être échangé à tout moment, de sorte qu'il n'a pas vraiment de sens pour avoir une valeur de l'heure du noyau, car si votre processus est bloqué pendant que le paquet réseau est entré, votre processus pourrait être bloqué plus longtemps, même s'il y a un paquet assis sur le tampon de réseau. Je crois que la carte réseau est capable de suivre le temps nécessaire à l'aide de la RTC à partir de l'interface BIOS, que le noyau n'a pas accès à son mode protégé.


Si le processus fonctionne avec une priorité en temps réel, il fonctionnera avant tous les processus avec une priorité inférieure indéfiniment. C'est à quel point la planification en temps réel fonctionne dans le noyau Linux - même sans un noyau de «temps réel». Ainsi, ce processus ne peut être préempté que par d'autres processus à la même priorité ou supérieure à celle utilisée par ce processus.



0
votes

Je pense que « quanta » est la meilleure théorie pour l'explication. Sur Linux, il est la fréquence de changement de contexte. Noyau donne à traiter le temps quanta. Mais processus est préempté dans deux situations:

  1. procédure de système d'appel de processus
  2. temps quanta est terminée
  3. interruption matérielle est comming (à partir du réseau, hdd, usb, horloge, etc ...)

    Temps de quanta non utilisé est attribué à un autre prêt à exécuter processus, en utilisant les priorités / rt etc.

    En fait, la fréquence de commutation de contexte est configuré à 10000 fois par seconde, il donne environ 100us pour quanta. mais la commutation de contenu prend un certain temps, il est dépendait cpu, voir ceci: http: // blog. tsunanet.net/2010/11/how-long-does-it-take-to-make-context.html i Do not understad, pourquoi la fréquence swith contenu est très élevé, mais il est pour le forum de discussion du noyau linux.

    problème partiellement similaire, vous trouverez ici: https: // serverfault.com/questions/14199/how-many-context-switches-is-normal-as-a-function-of-cpu-cores-or-other


1 commentaires

Comme il y a 2 commutateurs de contexte dans la partie mesurée de la boucle (Send () et RECV () Cause des interrupteurs de contexte), il semble peu probable d'expliquer le problème. Ajout d'un sommeil à l'extérieur ferait peu d'impact sur le temps dans la boucle.



0
votes

Si la quantité de données envoyée par l'application est suffisamment grande et assez rapide, elle pourrait remplir les tampons de noyau, ce qui conduit à un retard sur chaque envoi (). Comme le sommeil est en dehors de la section mesurée, il mangerait alors le temps qui serait autrement consacré à bloquer sur l'appel d'envoi ().

Un moyen d'aider à vérifier que ce cas serait de fonctionner avec un nombre relativement petit d'itérations, puis un nombre modéré d'itérations. Si le problème se produit avec un petit nombre d'itérations (disons 20) avec de petites tailles de paquets (disons <1k), ceci est probablement un diagnostic incorrect.

Gardez à l'esprit que votre processus et votre noyau peuvent facilement submerger l'adaptateur réseau et la vitesse du fil de l'Ethernet (ou d'un autre type de support) si vous envoyez des données dans une boucle serrée comme celle-ci.

J'ai du mal à lire les coups d'écran. Si Wireshark affiche une vitesse de transmission constante sur le fil, il suggère que c'est le bon diagnostic. Bien sûr, faire les mathématiques - diviser la cuisson de la taille de la paquette (+ en-tête) - devrait donner une idée de la vitesse maximale à laquelle les paquets peuvent être envoyés.

Quant aux 700 microsecondes menant à une augmentation du retard, c'est plus difficile à déterminer. Je n'ai aucune pensée sur celle-là.


2 commentaires

n'importe quoi? Je suis très curieux.


Je vais remettre ces systèmes et tester votre théorie. Cependant, je pense que les paquets étaient <1k, mais ne vous souviennent pas vraiment si cela s'est passé avec quelques interactions.



2
votes

OK, j'ai atteint une conclusion.

J'ai essayé mon programme à l'aide de Linux, au lieu de NetBSD, sur le serveur. Il a couru comme prévu, c'est-à-dire, peu importe combien je [nano] sommeil à ce point de ce point, le résultat est le même.

Ce fait me dit que le problème pourrait mentir dans le pilote d'interface de Netbsd . Pour identifier le pilote, j'ai lu la sortie dmesg . Ceci est la partie pertinente: xxx

Ainsi, comme vous pouvez le constater, mon interface est appelée wm0 . Selon Ce (page 9) Je devrais vérifier quel pilote est chargé en consultant le fichier sys / dev / pci / fichiers.pci , ligne 625 ( ici ). Il montre: xxx

puis, recherchez dans le code source du pilote ( dev / pci / if_wm.c , ici ), j'ai trouvé un extrait de code qui pourrait changer le comportement du conducteur. : xxx

puis j'ai changé cette valeur de 1500 sur 1 (essayer d'augmenter le nombre d'interruptions par seconde autorisées) et à 0 (essayer d'éliminer l'interruption de l'interruption), mais Ces deux valeurs produisaient le même résultat:

  • sans nanosleep: la latence de ~ 400 US
  • avec un nanosleep de 100 États-Unis: la latence de ~ 230 US
  • avec un nanosleep de 200 États-Unis: la latence de ~ 120 US
  • avec un nanosleep de 260 US: la latence de ~ 70 US
  • avec un nanosleep de 270 US: la latence de ~ 60 US (latence minimale que je pourrais atteindre)
  • avec un nanosleep de rien de plus de 300 US: ~ 420 US

    Ceci est, au moins mieux se comporter que la situation précédente.

    Par conséquent, j'ai conclu que le comportement est due au pilote d'interface du serveur. Je ne suis pas disposé à enquêter plus loin pour trouver d'autres coupables, car je passe de Netbsd à Linux pour le projet impliquant cet ordinateur de conseil unique.


1 commentaires

FYI à n'importe qui lisant cette réponse: la valeur donnée au registre WMREG_IRE dans la puce n'affectera que le comportement de la puce, et non le conducteur en soi, et cette valeur est définie plus ou moins en supposant une pièce jointe Gigabit. Dans le contexte plus large du code du pilote, il est également important de noter que le paramètre est appliqué uniquement sur 82543 et les nouveaux jetons, et à partir des commentaires de modification, l'objectif semble être d'éviter de verrouiller la puce. Un réglage supplémentaire peut être approprié pour des chipsets encore plus récents, mais évidemment, il n'est pas lié de quelque manière que ce soit au comportement décrit dans la question.



0
votes

J'ai un conseil sur la manière de créer une mesure de performance plus précise. Utilisez l'instruction RDTSC (ou même la fonction intrinsèque __RDTSC ()). Cela implique la lecture d'un compteur CPU sans quitter Ring3 (aucun appel système). Les fonctions de Gettime impliquent presque toujours un appel système qui ralentit les choses.

Votre code est un peu délicat car il implique 2 appels système (envoi / RECV), mais en général, il est préférable d'appeler le sommeil (0) avant la première mesure pour que la très courte mesure ne reçoit pas de contexte. changer. Bien sûr, le code de la mesure du temps (et du sommeil ()) doit être désactivé / activé via des macros dans les fonctions sensibles à la performance.

Certains systèmes d'exploitation peuvent être traqués de la priorité de votre processus en présentant une fenêtre de l'heure d'exécution de votre processus (par exemple. Sleep (0)). Lors de la prochaine annexe, le système d'exploitation (pas toutes) augmentera la priorité de votre processus car il n'a pas fini de fonctionner son quota d'exécution.


0 commentaires