2010-11-18 7 views
15

Je souhaite utiliser le minuteur de résolution le plus élevé possible en utilisant C#. Par exemple, je veux élever un événement toutes les onze ticks (j'ai entendu que tick est le plus haut compteur possible dans pc). J'ai essayé la minuterie et j'ai trouvé que le temps écoulé est en millisecondes. regardé chronomètre, mais le chronomètre ne soulève pas d'événements.Soulever un événement dans un intervalle/minuterie haute résolution

Merci.

+2

Pourquoi dans le monde voudriez-vous élever un événement toutes les onze ticks? Utilisez-vous ceci pour le profilage de code? –

+5

Vous ne répondez pas aux questions. 11 tick est juste un exemple. tout ce que je veux, c'est une minuterie haute résolution. si possible, jusqu'à 1 tick. et oui, je développe une application pour faire un benchmark. –

+0

C'est pourquoi je supporte les commentaires downvoting –

Répondre

18

L'utilisation d'une minuterie multimédia devrait vous donner environ 1000 événements par seconde. Ce code devrait vous aider sur le chemin.

 public delegate void TimerEventHandler(UInt32 id, UInt32 msg, ref UInt32 userCtx, UInt32 rsv1, UInt32 rsv2); 

    /// <summary> 
    /// A multi media timer with millisecond precision 
    /// </summary> 
    /// <param name="msDelay">One event every msDelay milliseconds</param> 
    /// <param name="msResolution">Timer precision indication (lower value is more precise but resource unfriendly)</param> 
    /// <param name="handler">delegate to start</param> 
    /// <param name="userCtx">callBack data </param> 
    /// <param name="eventType">one event or multiple events</param> 
    /// <remarks>Dont forget to call timeKillEvent!</remarks> 
    /// <returns>0 on failure or any other value as a timer id to use for timeKillEvent</returns> 
    [DllImport("winmm.dll", SetLastError = true,EntryPoint="timeSetEvent")] 
    static extern UInt32 timeSetEvent(UInt32 msDelay, UInt32 msResolution, TimerEventHandler handler, ref UInt32 userCtx, UInt32 eventType); 

    /// <summary> 
    /// The multi media timer stop function 
    /// </summary> 
    /// <param name="uTimerID">timer id from timeSetEvent</param> 
    /// <remarks>This function stops the timer</remarks> 
    [DllImport("winmm.dll", SetLastError = true)] 
    static extern void timeKillEvent( UInt32 uTimerID); 

Arrêtez ces minuteurs après les avoir exécutés. Ils sont assez lourds sur votre système *. Attrapez toutes les exceptions et ne les laissez pas échapper votre gestionnaire d'événements.

* Démarrer plus de 5 minuteurs ralentira sérieusement la plupart des systèmes! Exécutez le moins de code possible dans les gestionnaires d'événements et assurez-vous que le code d'exécution est plus rapide que 1 milliseconde ou que vous rencontrez de sérieux problèmes. J'ai commencé un délégué toutes les 10-50 ticks pour augmenter l'affichage de l'étiquette.

Un commutateur de thread normal qui se produit sur un Thread.Sleep laissera un thread-slot libre de votre code et prendra environ 40 millisecondes. Vous pouvez également augmenter la fréquence du commutateur de thread avec certains appels de noyau NT, mais s'il vous plaît, ne faites pas cela.

4

Les différentes classes de minuterie utilisent une plus grande granularité. Les deux Threading.Timer et Timers.Timer utilisent 1/64 seconde, ce qui est de 15,625 millisecondes. Si la «coche» à laquelle vous faites référence est la coche de 100 nanosecondes, utilisée par la classe DateTime, la classe TimeSpan et la sortie par le chronomètre, la longueur de 11 ticks dont vous parlez est de 1 100 nanosecondes, ou 1,1 microsecondes. Au meilleur de ma connaissance, il n'y a pas de minuterie intégrée qui vous donnera cette résolution. Si vous voulez vraiment qu'un événement se produise toutes les 1,1 microsecondes, vous devrez supprimer l'idée d'un 'timer', et penser plutôt en termes d'un court délai. Faites un fil de haute priorité et exécutez votre événement en boucle. N'appelez pas Thread.Sleep(), car je crois que 1,1 microsecondes est plus petit que le timeslice du planificateur du système. Vous devrez faire une boucle de retard à la place. En outre, sachez que l'intervalle de temps dont vous parlez est très, très petit. 1,1 microsecondes est seulement 2,200 cycles de processeur sur un processeur de 2 GHz. Pas un montant insignifiant, mais pas beaucoup de temps pour faire beaucoup de travail. Si vous parlez de la coche que vous avez dite dans votre commentaire, cela ne représente que 200 cycles de processeur: c'est à peu près le temps de faire quelques dizaines d'opérations mathématiques, et peut-être appeler une fonction.

+0

La coche qui m'intéresse est le chronomètre.frequency coche un peu. En d'autres termes, cycle du processeur coche. –

+3

Cochez le cycle du processeur, par exemple, 2 GHz? C'est 0,5 nanosecondes. Toutes sauf les instructions de processeur les plus simples prennent plusieurs cycles de processeur. Par exemple, vous pourriez être en mesure d'incrémenter un entier dans un cycle, mais une instruction 'if'? Pas du tout, c'est de l'ordre de 10 cycles de processeur, 5 nanosecondes. Il n'y a aucun moyen de vérifier une minuterie à cette fréquence, et encore moins de faire du travail. –

+4

Attends, je le reprends. Il existe un moyen d'avoir une minuterie avec une fréquence de 1 cycle de processeur: Réglez le thread en haute priorité et faites 'while (true) {...}' –

14

Tout d'abord, vous devez réaliser que il est extrêmement difficile, voire impossible, de faire la synchronisation exacte sur un ordinateur en raison des limitations exposées à la fois par le matériel et le logiciel. La bonne nouvelle est que ce genre de précision est rarement nécessaire. Dix ticks est un incroyablement petit quantité de temps. Très peu de travail est fait par le processeur dans cet intervalle, et il ne sera jamais statistiquement significatif. Pour référence, l'horloge Windows a une précision d'environ 10 millisecondes (moins sur les versions antérieures). Envelopper votre code avec des appels au DateTime.UtcNow ne va pas faire mieux que ça.

Dans votre question, vous parlez de vouloir «élever un événement». Le problème est que le seul type d'objet de chronométrage qui déclenche un événement à des intervalles spécifiques est l'objet Timer. Il est disponible en 3 versions différentes dans le .NET Framework (System.Timers.Timer, System.Threading.Timer et System.Windows.Forms.Timer), tous ayant leurs propres scénarios d'utilisation et leurs particularités, mais aucun ne garantit une précision proche de ce que vous demandez . Ils ne sont même pas conçus pour le faire, et il n'y a pas non plus de fonctions équivalentes exposées par l'API Windows qui fourniront ce type de précision.

La raison pour laquelle j'ai demandé pourquoi vous vouliez faire cela et si vous essayez de benchmark, c'est parce que cela change tout le jeu. Le .NET Framework (à partir de la version 2.0) fournit un Stopwatch object expressément conçu pour mesurer avec précision le temps écoulé pour une situation telle que l'analyse comparative ou le profilage des performances. Le Stopwatch enveloppe simplement les fonctions Windows API QueryPerformanceFrequency et QueryPerformanceCounter (ce qui devrait confirmer ma suggestion quant à son utilisation prévue). Nous avions l'habitude de P/Invoke ces fonctions pour accéder à ce type de fonctionnalité dans les versions antérieures du Framework, mais il est maintenant intégré. Si vous avez besoin d'un timer avec une résolution relativement élevée pour le benchmarking, le Stopwatch est votre meilleur pari . En théorie, il peut vous fournir un timing inférieur à la microseconde.

Mais ce n'est pas sans problèmes. Cela ne déclenche aucun événement, donc si votre conception actuelle repose sur la gestion d'événements, vous devrez la repenser. Et, il n'est pas garanti d'être parfaitement précis non plus. Bien sûr, il peut avoir la résolution la plus élevée possible en raison de contraintes matérielles, mais cela ne veut pas dire nécessairement qu'il sera conforme à vos exigences. Par exemple, il peut ne pas être fiable sur un système à plusieurs processeurs où Start et Stop doivent être exécutés sur le même processeur. Cela ne devrait pas avoir d'importance, mais it does. C'est aussi subject to being unreliable sur les processeurs qui peuvent réduire leur vitesse d'horloge de haut en bas. Et oserais même mentionner que l'appel à QueryPerformanceCounter lui-même va prendre un certain temps-environ 5 microsecondes même sur un processeur moderne de 2+ GHz, ce qui vous empêche d'être en mesure d'atteindre ce moment sous-microseconde qui semblait bien en théorie. Là encore, tout profileur de code raisonnable considérerait cette quantité de temps négligeable parce que, bien, c'est.
(Voir aussi: http://www.devsource.com/c/a/Techniques/High-Performance-Timing-under-Windows/2/)

+0

Merci pour l'explication long. Si, par exemple, j'utilise un os temps réel, puis-je obtenir ce genre de minuteur de haute précision? Windows Server est un OS temps réel? Merci. –

+2

Si vous utilisez un RTOS, vous pourriez avoir une chance d'avoir une minuterie haute résolution. Cependant, un système d'exploitation en temps réel ne signifie pas que les choses arrivent immédiatement, mais qu'elles se produisent dans une limite de temps connue, de sorte que vous ne pouvez toujours pas être aussi précis que vous le pensez. Quant à Windows étant un RTOS, certainement pas. –

+1

@publicENEMY: La réponse de David Yaw est correcte. Même les versions Server de Windows ne sont pas des systèmes d'exploitation en temps réel (je lance Windows Server en tant que station de travail, ce n'est pas très différent). Il convient également de souligner que même si vous avez surmonté les limites du logiciel exposées par le système d'exploitation, vous devrez toujours faire face aux limites du matériel PC commun. Si vous envisagez de changer de système d'exploitation, il doit y avoir * une * meilleure façon de faire ce que vous essayez d'accomplir. –

Questions connexes