hochfrequentes Timing

8

Ich möchte einen hochfrequenten Callback-Thread erstellen. Im Wesentlichen benötige ich eine Funktion, die in einem regelmäßigen Hochfrequenzintervall (bis zu 100 Hz) ausgeführt wird. Ich merke, dass Windows eine normale Thread-Ausführung Scheibe hat ~ 15ms. Ich möchte ein reguläres Intervall angeben, das schneller als 15ms sein kann.

Das versuche ich zu erreichen. Ich habe ein externes Gerät, das in einem bestimmten Intervall gemeldet werden muss. Das Intervall ist abhängig von der Situation variabel. Ich erwarte, dass ich nie mehr als 100Hz (10ms) Nachrichtenrate benötigen würde.

Ich kann natürlich eine Spin-Schleife implementieren, aber ich hatte gehofft, dass es eine Lösung gibt, die nicht so viel verschwendete Ressourcen erfordert.

Die bereitgestellten Links zu Fragen / Antworten lösen diese Frage nicht. Während ich zustimme, dass die Frage auf verschiedene Arten gestellt wurde, gab es keine gute Lösung, die das Problem tatsächlich gelöst hat.

Die meisten Antworten beziehen sich auf die Verwendung der Stoppuhr und das manuelle Ausführen der Timing-Task, die zu CPU-intensiv ist. Die einzigen brauchbaren Lösungen waren die Multimedia-Timer, die, wie Haans erwähnte, einige Fallstricke aufwiesen. Ich habe jedoch eine andere Lösung gefunden, die ich unten hinzufügen werde. Ich kenne die Fallstricke zu diesem Zeitpunkt nicht, aber ich plane, einige Tests und Nachforschungen zu machen. Ich bin immer noch an Kommentaren zur Lösung interessiert.

WINAPI Anruf über

%Vor%

und

%Vor%

Link - Ссылка Ich verwende PInvoke, um dies zu erreichen. Dies wäre jedoch auch im Umgang mit den Multimedia-Timern erforderlich.

Meine PInvoke-Signatur für Interessierte. Pinvoke-Link

%Vor%

Verwenden Sie CreateTimerQueueTimer, um einen Timer-Rückruf zu starten. Verwenden Sie DeleteTimerQueueTimer, um den Timer-Rückruf zu stoppen. Dies ist etwas flexibel, da Sie auch benutzerdefinierte Warteschlangen erstellen können. Die einfachste Implementierung, wenn nur eine einzige Instanz benötigt wird, wäre jedoch die Verwendung der Standardwarteschlange.

Ich testete diese Lösung entlang Seite eins mit der Stoppuhr mit einer Spin-Schleife und die Ergebnisse, die ich in Bezug auf das Timing erhielt, waren fast identisch. Die CPU Load war jedoch auf meinem Rechner deutlich anders.

Stoppuhr mit Spin-Loop - ~ 12-15% konstante CPU-Last (ca. 50% eines meiner Kerne) CreateTimerQueueTimer - ~ 3-4% konstante CPU-Last

Ich glaube auch, dass die Codepflege mit der CreateTimerQueueTimer-Option reduziert wird. da es nicht erforderlich ist, Logik zu Ihrem Code-Flow hinzuzufügen.

    
galford13x 19.05.2013, 00:12
quelle

4 Antworten

10

Es gibt eine Menge von ungenauen Informationen, die durch die Links und Kommentare verbreitet werden. Ja, standardmäßig ist der Takt-Tick-Interrupt 1/64 Sekunden = 15.625 ms auf den meisten Maschinen, aber er kann geändert werden. Auch der Grund, dass einige Maschinen mit einer anderen Geschwindigkeit arbeiten. Die Windows-Multimedia-API, die von winmm.dll zur Verfügung steht, ermöglicht es Ihnen, daran zu basteln.

Was Sie nicht tun möchten, ist die Verwendung einer Stoppuhr. Sie erhalten nur dann eine genaue Intervallmessung, wenn Sie es in einer Hot-Loop verwenden, die ständig prüft, ob das Intervall abgelaufen ist. Windows behandelt einen solchen Thread unfreundlich, wenn sein Quantum abläuft. Der Thread wird nicht für eine Weile neu geplant, wenn andere Threads um den Prozessor konkurrieren. Dieser Effekt ist leicht zu übersehen, da Sie Ihren Code normalerweise nicht mit anderen aktiven Prozessen debuggen und CPU-Zeit verbrauchen.

Die Funktion, die Sie verwenden möchten, ist timeSetEvent (), sie bietet einen sehr genauen Timer, der bis zu 1 Millisekunde lang sein kann. Es ist selbstkorrigierend und verringert das Intervall, wenn es notwendig (und möglich) ist, aufzuholen, wenn der vorherige Rückruf aufgrund von Planungseinschränkungen verzögert wurde. Beachten Sie jedoch, dass es schwierig zu verwenden ist, da der Rückruf aus einem Threadpool-Thread besteht, ähnlich wie System.Threading.Timer. Achten Sie also darauf, sichere Verriegelung zu verwenden und Gegenmaßnahmen zu ergreifen, die sicherstellen, dass Sie keine Probleme mit der erneuten Eingabe haben.

Ein völlig anderer Ansatz ist timeBeginPeriod (), er ändert die Taktunterbrechungsrate. Das hat viele Nebenwirkungen, denn ein Thread.Sleep () wird genauer. Das ist die leichtere Lösung, da Sie das synchron machen können. Schlafe einfach für 1 ms und du bekommst die Interrupt Rate. Ein Code, mit dem man spielen kann, zeigt, wie es funktioniert:

%Vor%

Ausgabe an meinem Rechner:

%Vor%

Seien Sie jedoch vorsichtig mit diesem Ansatz, wenn ein anderer Prozess auf Ihrem Rechner die Taktunterbrechungsrate bereits unter 10 ms gesenkt hat, dann erhalten Sie keine Sekunde mehr aus diesem Code. Das ist sehr umständlich zu handhaben, man kann es nur wirklich sicher machen, indem man nach einer Rate von 1 ms fragt und 10 Stunden schläft. Betreiben Sie das nicht auf einer batteriebetriebenen Maschine. Begünstige timeSetEvent ().

    
Hans Passant 19.05.2013, 08:48
quelle
1

Vielleicht möchten Sie das StopWatch ausprobieren, das dasselbe hi verwendet -performance Timer von DirectX verwendet.

    
Ali B 19.05.2013 00:26
quelle
0

Beachten Sie, dass ein Teil des Grundes, warum 15ms die effektive minimale Sleep -Zeit ist, darin besteht, dass es diese Größenordnung annehmen kann, um Ihren Prozess auszuwechseln, einen anderen auszutauschen, etwas Zeit zu haben, etwas zu tun, zu tauschen es aus und tauschen Sie wieder ein. Unter der Annahme, dass Sie mehrere Kerne haben, ist es wahrscheinlich am besten, wenn Sie Ihren 100 Hz Updater handverlesen haben, besonders wenn Sie wissen, wann Sie ein paar Zyklen für eine manuell generierte Speicherbereinigung überspringen möchten und / oder wie vorgeschlagen C / C ++ verwenden. p>     

Mark Hurd 19.05.2013 06:53
quelle
0

Thread.Sleep() scheint nicht so ungenau zu sein, wie Sie vielleicht in so kleinen Abständen denken, Die folgenden auf Windows 8.1 x64 i7 Laptop .NET 4.0:

%Vor%

Ausgabe:

  

C # .NET 4.0 durchschn. 0.00197391928071928

Also knapp 2ms Intervall! Ich nehme an, wenn es mehr Konflikte gab, die ziemlich schnell steigen könnten, obwohl ich eine Reihe von Apps geöffnet habe, einfach nichts wegschleifen.

Beim Schlafen für 10ms, d. h. 100Hz, habe ich einen Durchschnitt von 0,0109 ... ziemlich genau!

    
markmnl 20.02.2014 07:16
quelle