ungenaue Timers?

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 13 Antworten in diesem Thema. Der letzte Beitrag () ist von hal2000.

    ungenaue Timers?

    Guten Abend an alle!

    Gestern musste ich ein Programm schreiben, das jede 100 ms exakt was macht.
    Erster Versuch mit den Timers, die man unter Toolbox findet und habe den Intervall zwischen Ticcs gemessen.
    Timer war eing. auf 100ms aber Intervall sagte immer etwas über 120 und nie konstant, immer kam einen anderen Wert (111, 113,etc). Ich kenne richtig nur die Timer in C, und da sind die ziemlich exakt. Ich konnte mich nicht vorstellen wieso solche Ungenauigkeit (über 10%) vorkommt, denn 100ms sind für ein PC eig. eine Ewigkeit. Dachte immer den "Timer" wird bei einem eigenenThread hochgezählt, so dass egal was das Programm macht, soll eig genau ticken. Ich machte auch gleichzeitig ein StressTest (HollyLoad), da ich dachte evtl ist es so, weil PC mit etwas beschäftigt ist, aber mit laufenden StressTest, waren die Intervalle genau so ungenau, nicht mehr und nicht weniger.

    Ausserdem wenn ich mir das Intervall (als Timespan.totalseconds.ToString) anzeigen lasse, ab und zu kommen ganze Zahlen, ab und zu 3 Kommastellen dazu und ab und zu sogar 7 Kommastellen. Da muss ich doch was falsch machen oder?

    Sind da die verschiedene Timers in VB "verschieden" bezüglich die Genauigkeit?
    Woher kommt diese Ungenauigkeit in diesem Fall?

    Hoffe ihr könnt mir helfen, da ich bei Google nicht viel über den Grund gefunden habe.

    Gruß:)
    Life doesn't give you a datasheet. Sometimes the docs are wrong and you have to try it.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „rgomez“ ()

    @rgomez Definiere
    jede 100 ms exakt
    Windows ist kein Echtzeit-Betriebssystem, viele Anwendungen teilen sich den Prozessor und ggf. den RAM.
    Auch wenn der Multimedia-Timer (läuft auf der Grafikkarte) genauer ist als die Timer, die auf dem Prozessor laufen, laufen auch die halt unter Windows.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @RodFromGermany Danke dir für den Hinweis. Was wäre deine Empfehlung für solche Programme zu realisieren? Welche OS und welcher Programmiersprache?
    Life doesn't give you a datasheet. Sometimes the docs are wrong and you have to try it.
    Ich brauche ein Digitales Multimeter (DMM) so exakt wie möglich jede 100ms und wieder jede 50ms abfragen. Die Werte mit jeweiligen Zeit in ms werden dann in einen Excel oder Text Datei exportiert. Die Zeit muss richtig sein, da es sich um etwas "offizieles" handelt und da können die Intervalle nicht immer anders sein, den das ist für die Messung nicht fachlich korrekt, und noch weniger für einen Prüfprotokoll.
    Grund ist ein Fehler zu finden bei Drucktransmittern, die eig. irgendwann für sehr kurze Zeit ausfallen und deswegen in der Anlage Alarm ausgelöst wird.
    Mit einen uC wäre kein Problem, da ich mich damit auskenne und eig. spricht nichts dagegen. Wollte aber trotzdem wissen, woher diese Ungenauigkeit kommt und wäre schon wenn ich genaue Zeitintervalle auch bei Windows Forms, ohne zusätzlichen Hardware hinkriegen kann.
    Life doesn't give you a datasheet. Sometimes the docs are wrong and you have to try it.

    rgomez schrieb:

    genaue Zeitintervalle auch bei Windows Forms
    geht mit der von Dir geforderten Genauigkeit eigentlich nicht.
    Ein Hardware-Timer, µC eben, der das Auslesen anstößt und WinForms empfängt die Daten wäre die beste Lösung.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    rgomez schrieb:

    Die Zeit muss richtig sein, da es sich um etwas "offizieles" handelt

    In diesem Fall benötigst du unbedingt einen Hardware-Timer, der (wichtig) auch die Messung selbst vornimmt und speichert. Erst die Auswertung des Messdatenstroms ist nicht mehr zeitkritisch.

    rgomez schrieb:

    Wollte aber trotzdem wissen, woher diese Ungenauigkeit kommt und wäre schon wenn ich genaue Zeitintervalle auch bei Windows Forms, ohne zusätzlichen Hardware hinkriegen kann.

    Die Ungenauigkeit kommt vom Betriebssystem und dessen Prozessverwaltung (Scheduler). Selbst mit einem Hardware-Timer am Rechner kommst du nicht weiter: Der Timer löst einen Interrupt aus. Dessen Service-Routine läuft zwar sofort, aber deren Ergebnis ist nur die Anweisung an den Scheduler, dass der Prozess, der das Timer-Event abonniert hat, irgendwann mal wieder Rechenzeit bekommen sollte. In der Zwischenzeit werden z.B. erst Netzwerkpakete verarbeitet, der Monitor geupdatet (Grafikkarte), Maus & Tastatur ausgewertet, ... . Irgendwann ist dann der Messprozess dran, üblicherweise mit mehreren Millisekunden Verzögerung. Er fährt mit dem Event Handler des Timers fort und liest einen Messwert vom Multimeter - da ist die Verzögerung aber schon lange passiert. Dazu kommt, dass das Abholen der Messwerte des Multimeters üblicherweise ein Systemaufruf ist - dabei nimmt der Scheduler dem Messprozess erneut den Prozessor weg, weil der Systemaufruf auch von einem Interrupt bearbeitet wird. Und natürlich kommen ggf. erst wieder alle anderen Prozesse dran, die auch rechnen wollen, bevor der Messprozess seinen Messwert überhaupt lesen darf.

    Die Ursache der Verzögerung liegt also auf einer sehr viel tieferen Abstraktionsschicht als Windows Forms - auf normalen Systemen ist "zeitliche Genauigkeit" nicht machbar. Die Art von Programm macht dabei keinen Unterschied - ob das nun Windows Forms, WPF, Qt, GTK oder ne banale Konsolenanwendung ist. Ein Lösungsansatz wäre ein Echtzeit-Betriebssystem (siehe @Manawyrm). Dessen Scheduler sichert dir in gewissen Grenzen für bestimmte Prozesse zeitliche Exaktheit zu.

    Auf einem µC funktioniert die Sache, weil dort quasi das ganze Programm als einzige große Interrupt-Serviceroutine läuft (vorausgesetzt es läuft kein Kernel mit Scheduler).
    Gruß
    hal2000
    Vielen Dank @hal2000. Mittlerweile verstehe auch den Grund.

    Hätte aber noch eine Frage und zwar, wenn ich ein Stopwatch starte, gefolgt von Thread.Sleep(100) und Stopwatch.stop, dann wird Stopwatch.Elapsed ziemlich genau (100 oder 99ms). Heisst das wenn ich etwa:

    VB.NET-Quellcode

    1. Sub main()
    2. For i as Long = 0 To 1000000
    3. mydmm.write(xxx)
    4. Console.WriteLine(mydmm.readTo(vbCrLf))
    5. System.Thread.Sleep(100)
    6. Next


    kriege ich mind. eine "exakte" Zeitspanne zwischen 2 Durchläufe? Oder jeder Durchlauf benötigt nicht gleich viel Zeit, d.h ist abhängig auch von "wie beschäftigt der Computer ist"?

    Was ich auch nicht ganz verstehe dann, ist warum, wenn ich das Intervall gemessen habe als ich gleichzeitig ein StressTest für die CPU laufen gelassen habe, die Werte , also das Intervall nicht größer wurde als ohne StressTest, da eig. sollte der CPU zwischen Messung und Messung ja richtig viel zu tun haben (100% CPU-Last beim Stress Test).
    Life doesn't give you a datasheet. Sometimes the docs are wrong and you have to try it.
    Die Stopwatch macht folgendes:
    - Start: Schaue auf eine (sehr genaue) Uhr und speichere die Zeit
    - Stop: Schaue erneut auf die Uhr
    - Elapsed: Berechne die Differenz.

    Thread.Sleep(100) leitet mehr oder weniger direkt an einen Systemaufruf weiter, der z.B. ein RTC-Event registriert. Auf jeden Fall verlässt der aktuelle Thread den Prozessor. Wenn der Rechner jetzt zufällig nach genau 100 ms nichts zutun hat, kommt der Thread wieder dran und die 100 ms sind einigermaßen genau. Im Allgemeinen wird die Zeit aber größer als 100 ms sein. Hier steht, dass intern die API-Funktion SleepEx zum Einsatz kommen könnte. In deren Doku steht direkt wieder, was ich bereits geschrieben habe:
    After the sleep interval has passed, the thread is ready to run. [...] Note that a ready thread is not guaranteed to run immediately. Consequently, the thread may not run until some time after the sleep interval elapses.


    rgomez schrieb:

    das Intervall nicht größer wurde als ohne StressTest

    Das würde ich unter Zufall verbuchen, oder unter "schlechter Stresstest". Bei einem echten Stresstest, z.B. mit Prime95, beginnt sogar die Inputverarbeitung (Maus) zu stocken. Erst ab diesem Auslastungsgrad halte ich solche Aussagen für sinnvoll. Gerade aktuelle CPUs haben oft noch sporadisch eine freie Einheit, auch unter hoher Last. Nicht umsonst werden zur Steigerung der Auslastung einzelne Kerne virtuell als 2 Kerne an das System gemeldet (HyperThreading).

    Und selbst wenn Thread.Sleep genauer sein sollte: Der Fehler summiert sich. Bei einer Abweichung von nur 1 ms (99 <-> 100, wie bei dir) liegst du nach 100 Messungen schon 100 ms daneben, was ziemlich schlecht ist.
    Gruß
    hal2000

    Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von „hal2000“ ()

    hal2000 schrieb:

    Der Fehler summiert sich
    Man könnte zu diesem Zweck einen regelmässigen Abgleich gegen den Tick-Count machen, um die langfristigen Fehler auszubügeln.
    Kurzfristige Unstimmigkeiten lassen sich damit aber nicht beheben.

    Ich frage mich allerdings, wie lange so ein Ausfall eines Drucktransmitters dauert und ob man da nicht auch mit ein paar Millisekunden Toleranz noch zuverlässige Erkenntnisse bekommt.
    --
    If Not Program.isWorking Then Code.Debug Else Code.DoNotTouch
    --
    Vielleicht genügt es ja schon, wenn die Zeit bekannt ist. Der Timer darf gerne ein wenig ungenau sein, wenn die Messfunktion den absoluten Zeitpunkt der Messung jeweils speichert. Über Performance Counter lässt sich dieser sehr genau ermitteln. Die Ungenauigkeit kann man dann rausrechnen, weil in dieser Anwendung nur zwischen "sinnvoller Messwert" und "nicht sinnvoller Messwert" unterschieden werden muss.
    Gruß
    hal2000