2010-11-18 4 views
15

Ich möchte den höchstmöglichen Auflösungstimer mit C# verwenden. Zum Beispiel möchte ich ein Ereignis alle 11 Ticks auslösen (ich habe gehört, dass Tick der höchstmögliche Zähler in PC ist). Ich habe Timer versucht und festgestellt, dass die Mindestlaufzeit in Millisekunden liegt. schaute auf Stoppuhr, aber Stoppuhr wirft keine Ereignisse auf.Ereignis in Hochauflösungsintervall erhöhen/Timer

Danke.

+2

Warum in der Welt möchten Sie alle 11 Ticks ein Ereignis auslösen? Verwenden Sie dies für die Code-Profilerstellung? –

+5

Sie beantworten keine Fragen. 11 Tick ist nur ein Beispiel. alles was ich will ist ein hochauflösender Timer. wenn möglich, bis zu 1 Tick. und ja, ich entwickle eine Anwendung, um eine Benchmark zu machen. –

+0

Deshalb unterstütze ich Downvoting-Kommentare –

Antwort

18

Mit einem Multimedia-Timer sollten Sie etwa 1000 Ereignisse pro Sekunde geben. Dieser Code sollte Ihnen auf dem Weg helfen.

 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); 

Stoppen Sie diese Timer nach dem Ausführen von ihnen. Sie sind ziemlich schwer auf Ihrem System *. Fange alle Ausnahmen und lass sie nicht von deinem Event-Handler entkommen.

* Starten von mehr als 5 Timern wird die meisten Systeme ernsthaft verlangsamen! Führen Sie so wenig Code wie möglich in den Ereignisprozeduren aus und stellen Sie sicher, dass der Ausführungscode schneller als 1 Millisekunde ist oder schwerwiegende Probleme auftreten. Ich habe alle 10-50 Ticks mit einem Delegierten begonnen, um die Beschriftung zu erhöhen.

Ein normaler Thread-Schalter, der auf einem Thread.Sleep auftritt, lässt einen Thread-Steckplatz frei von Ihrem Code und dauert etwa 40 Millisekunden. Sie können auch die Threadwechselfrequenz mit einigen NT-Kernelaufrufen erhöhen, aber tun Sie das bitte nicht.

4

Die verschiedenen Timer-Klassen verwenden eine größere Granularität. Sowohl Threading.Timer als auch Timer.Timer verwenden 1/64 Sekunde, was 15.625 Millisekunden entspricht.

Wenn das "Häkchen", auf das Sie sich beziehen, das 100-Nanosekunden-Häkchen ist, das von der DateTime-Klasse, der TimeSpan-Klasse und der Stoppuhr verwendet wird, dann beträgt die 11-Tick-Länge 1.100 Nanosekunden 1,1 Mikrosekunden. Soweit ich weiß, gibt es keinen integrierten Timer, der Ihnen diese Auflösung bietet. Wenn Sie wirklich wollen, dass alle 1.1 Mikrosekunden ein Ereignis eintritt, müssen Sie die Idee eines "Timers" weglassen und stattdessen in Form einer kurzen Verzögerung denken. Weisen Sie einem Thread hohe Priorität zu und führen Sie Ihr Ereignis in einer Schleife aus. Rufen Sie Thread.Sleep() nicht auf, da ich glaube, dass 1,1 Mikrosekunden kleiner als die Zeitscheibe des Systemplaners ist. Sie müssen stattdessen eine Verzögerungsschleife erstellen.

Beachten Sie auch, dass das Zeitintervall, nach dem Sie fragen, sehr, sehr klein ist. 1,1 Mikrosekunden sind nur 2.200 Prozessorzyklen auf einem 2 GHz-Prozessor. Nicht eine unbedeutende Menge, aber nicht viel Zeit, um viel Arbeit zu erledigen. Wenn Sie den 1 Tick sprechen, den Sie in Ihrem Kommentar gesagt haben, sind es nur 200 Prozessorzyklen: Das ist genug Zeit, um ein paar Dutzend mathematische Operationen durchzuführen und vielleicht eine Funktion aufzurufen.

+0

Das Häkchen, das mich interessiert, ist die Stoppuhr.Frequenz ein Tick. Mit anderen Worten, Prozessorzyklus Tick. –

+3

Prozessorzyklus Tick, z. B. 2 GHz? Das sind 0,5 Nanosekunden. Alle außer den einfachsten Prozessoranweisungen benötigen mehrere Prozessorzyklen. Zum Beispiel könnten Sie eine ganze Zahl in einem Zyklus erhöhen, aber eine 'if'-Anweisung? Nein, das liegt in der Größenordnung von 10 Prozessorzyklen, 5 Nanosekunden. Es gibt keine Möglichkeit, einen Timer auf dieser Frequenz zu überprüfen, geschweige denn eine Arbeit zu erledigen. –

+4

Warte, ich nehme das zurück. Es gibt eine Möglichkeit, einen Timer mit einer Frequenz von 1 Prozessorzyklus zu haben: Stellen Sie den Thread auf hohe Priorität und tun Sie 'while (true) {...}' –

14

Zunächst einmal müssen Sie erkennen, dass es ist extrem schwierig, wenn nicht unmöglich, genaues Timing auf einem Computer aufgrund von Einschränkungen sowohl durch die Hardware und Software ausgesetzt. Die gute Nachricht ist, dass diese Art von Präzision selten notwendig ist. Zehn Ticks ist eine wahnsinnig kleine Menge an Zeit. In diesem Intervall wird von der CPU sehr wenig Arbeit geleistet, und sie wird niemals statistisch signifikant sein.

Als Referenz hat die Windows-Uhr eine Genauigkeit von etwa 10 Millisekunden (weniger bei früheren Versionen). Das Einpacken Ihres Codes mit Anrufen an DateTime.UtcNow wird nicht besser machen.

In Ihrer Frage sprechen Sie darüber, dass Sie "ein Ereignis auslösen" wollen. Das Problem besteht darin, dass der einzige Typ des Zeitmessobjekts, das ein Ereignis in bestimmten Intervallen auslöst, das Objekt Timer ist. Es ist verfügbar in 3 verschiedenen Inkarnationen im .NET Framework (System.Timers.Timer, System.Threading.Timer und System.Windows.Forms.Timer), die alle ihre eigenen einzigartigen Szenarien und relative Macken haben, aber keine von ihnen garantieren Genauigkeit überall in der Nähe, was Sie für fragen . Sie sind nicht einmal dafür ausgelegt, und es gibt auch keine entsprechenden Funktionen, die von der Windows-API bereitgestellt werden, die diese Art von Genauigkeit bieten.

Der Grund, warum ich gefragt habe, warum du das machen wolltest und ob du einen Benchmark suchst, liegt daran, dass sich das ganze Spiel verändert. Das .NET Framework (ab Version 2.0) bietet eine Stopwatch object, die ausdrücklich dafür ausgelegt ist, die verstrichene Zeit für eine Situation wie Benchmarking oder Performance Profiling genau zu messen. Die Stopwatch umschließt einfach die Windows-API-Funktionen QueryPerformanceFrequency und QueryPerformanceCounter (was meinen Vorschlag hinsichtlich seiner beabsichtigten Verwendung bestätigen sollte). Früher mussten wir diese Funktionen aufrufen, um auf diese Art von Funktionalität in früheren Versionen des Frameworks zugreifen zu können, aber jetzt ist es bequem eingebaut. Wenn Sie einen Timer mit einer relativ hohen Auflösung für das Benchmarking benötigen, ist die Stopwatch Ihre beste Wahl . In der Theorie kann es Ihnen mit Sub-Mikrosekunden Timing zur Verfügung stellen.

Aber es ist nicht ohne seine Probleme. Es werden keine Ereignisse ausgelöst. Wenn Ihr aktuelles Design also auf der Ereignisbehandlung beruht, müssen Sie es neu überdenken. Und, ist es auch nicht garantiert, genau zu sein. Sicher, es kann die höchstmögliche Auflösung Hardware-Einschränkungen haben, aber das bedeutet nicht, dass es Ihre angegebenen Anforderungen erfüllen wird. Zum Beispiel kann es auf einem Mehrprozessorsystem unzuverlässig sein, wo Start und Stop auf demselben Prozessor ausgeführt werden müssen. Es sollte nichts ausmachen, aber it does. Es ist auch subject to being unreliable auf Prozessoren, die ihre Taktgeschwindigkeit auf und ab drosseln können. Und ich wage sogar zu erwähnen, dass der Anruf bei QueryPerformanceCounter selbst einige Zeit in Anspruch nimmt - etwa 5 Mikrosekunden sogar auf einem modernen 2+ GHz-Prozessor, was verhindert, dass Sie das in der Theorie gut bekannte Zeitfenster von unter einer Mikrosekunde tatsächlich erreichen können. Andererseits würde jeder vernünftige Code-Profiler diese Menge an Zeit als vernachlässigbar betrachten, denn, nun, ist es.
(Siehe auch: http://www.devsource.com/c/a/Techniques/High-Performance-Timing-under-Windows/2/)

+0

Danke für die ausführliche Erklärung. Wenn ich zum Beispiel Echtzeit os verwende, kann ich einen solchen Präzisions-Timer bekommen? ist Windows Server ein Echtzeit os? Vielen Dank. –

+2

Wenn Sie ein RTOS verwenden, haben Sie möglicherweise die Chance auf einen Timer mit hoher Auflösung. Ein Echtzeit-Betriebssystem bedeutet jedoch nicht, dass die Dinge sofort passieren, sondern dass sie innerhalb eines bekannten Zeitlimits stattfinden, so dass Sie immer noch nicht in der Lage sind, so präzise zu planen, wie Sie denken. Wie für Windows ein RTOS, definitiv nicht. –

+1

@publicENEMY: David Yaws Antwort ist korrekt. Selbst die Server-Versionen von Windows sind keine Echtzeit-Betriebssysteme (ich betreibe Windows Server als Workstation, es ist nicht viel anders). Es ist auch erwähnenswert, dass Sie selbst dann, wenn Sie die Software-Einschränkungen des Betriebssystems überwunden haben, immer noch mit den Einschränkungen gängiger PC-Hardware zu kämpfen haben. Wenn Sie einen Wechsel des Betriebssystems in Betracht ziehen, muss * * eine bessere Möglichkeit sein, das zu erreichen, was Sie erreichen wollen. –