Industrielle Fertigung
Industrielles Internet der Dinge | Industrielle Materialien | Gerätewartung und Reparatur | Industrielle Programmierung |
home  MfgRobots >> Industrielle Fertigung >  >> Industrial Internet of Things >> Eingebettet

So stellen Sie die beste Leistung der Qt-Zustandsmaschine sicher

Wenn Sie Qt für die Anwendungsentwicklung und Zustandsautomaten verwenden, verwenden Sie wahrscheinlich das Qt-Zustandsautomaten-Framework. Sie definieren die Zustandsmaschine also mit einfachem C++ oder SCXML. Ein alternativer Ansatz besteht darin, C++-Code aus Zustandsmaschinendiagrammen zu generieren. Dieser Artikel vergleicht diese Ansätze unter Berücksichtigung von Funktionalität, Anwendbarkeit und Leistung.

Ich wette, dass Sie als Softwareentwickler bereits jede Menge mehr oder weniger komplizierte Switch-Case-Anweisungen implementiert haben. Dies gilt zumindest für mich, und ein Großteil dieser Switch-Case-Codierung war im Wesentlichen nichts anderes als die Implementierung verschiedener Zustandsautomaten. Dies ist der einfachste Weg, um mit der Programmierung von Zustandsautomaten zu beginnen, wenn Sie nichts anderes zur Hand haben als die Programmiersprache Ihrer Wahl. Während der Start einfach ist, wird ein solcher Code mit zunehmender Komplexität der Zustandsmaschine immer weniger wartbar. Am Ende werden Sie überzeugt sein, dass Sie Zustandsautomaten nicht weiterhin manuell auf diese Weise implementieren möchten. (Übrigens – ich gehe davon aus, dass Sie wissen, was Zustandsautomaten sind.)

Implementieren von Zustandsautomaten

Es gibt verschiedene Alternativen, um Zustandsautomaten zu implementieren. Eine der besseren Möglichkeiten – insbesondere wenn Sie eine objektorientierte Programmiersprache wie C++ verwenden – ist die Anwendung des Zustandsmusters. Dieser Ansatz verwendet Zustandsklassen und oft auch Übergangsklassen. Eine Zustandsmaschine wird dann definiert, indem Instanzen von Zustandsklassen erzeugt und diese unter Verwendung von Instanzen von Übergangsklassen verbunden werden. In diesem Fall hilft ein Framework sehr, die Codegröße und den Implementierungsaufwand zu reduzieren.

Das Qt-State-Machine-Framework ist ein gutes Beispiel. Mit dieser API können Sie eine Zustandsmaschine mit kompaktem Code „konfigurieren“. Sie müssen sich nicht um Details der Semantik der Zustandsmaschinenausführung kümmern, da diese bereits vom Framework implementiert werden. Sie müssen immer noch Code schreiben, und da Ihre Zustandsmaschine komplexer wird und einige Dutzend oder sogar Hunderte von Zuständen enthält, wird es wirklich schwierig, sich einen Überblick zu verschaffen. Ein Bild sagt mehr als tausend Worte und das bekannte Konzept der Zustandsdiagramme hilft, diese Einschränkung zu überwinden. Qt selbst bietet Unterstützung für State Chart XML (SCXML), einem W3C-Standard. Da das Schreiben von XML von Hand keinen Spaß macht,Qt Creator enthält auch einen einfachen grafischen Statechart-Editor.

Unabhängig vom konkreten Implementierungsansatz ist die Verwendung einer grafischen Syntax die beste Wahl, um Zustandsautomaten zu bearbeiten und zu verstehen. Solche grafischen Modelle können nicht nur durch Sprachen wie SCXML textuell dargestellt werden, sondern können auch verwendet werden, um jede Art von Quellcode in Programmiersprachen zu generieren, wie beispielsweise eine auf Schalterfällen basierende Zustandsmaschine in einfachem C++ – oder C++-Code, der Instanzen von QStateMachine. Wenn Sie ein Tool verwenden, das eine solche Transformation für Sie vornimmt, können Sie das mühsame Schreiben von State-Machine-Code vermeiden. Es hebt alle drei Implementierungsansätze auf das gleiche Usability-Niveau. Dennoch unterscheiden sich die Implementierungen noch grundlegend. In diesem Artikel geht es darum, ihr Laufzeitverhalten und insbesondere ihre Leistung zu vergleichen.


UnsplashPhoto by Austris Augusts auf Unsplash

Die Konkurrenten

Wie sieht es also mit der Leistung aus? Wie unterscheiden sich die verfügbaren Ansätze bezüglich benötigter CPU-Zyklen? Um konkrete Zahlen zu erhalten, habe ich eine Performance-Testsuite eingerichtet. Der erste Teil vergleicht die verschiedenen Umsetzungsstrategien. Dies sind die Konkurrenten:

  1. SCXML-Interpreter – die Testzustandsmaschine wird mit SCXML definiert und von Qts QSCXMLStateMachine ausgeführt Klasse.
  2. Zustandsmuster – die Testzustandsmaschine wird mit QStateMachine implementiert Klassen.
  3. Einfacher C++-Code – die Testzustandsmaschine wird durch eine C++-Klasse implementiert, die einen grundlegenden, auf Schalterfällen basierenden Ansatz anwendet.

Hinweis:Code für diese Beispiele finden Sie hier.

Die ersten beiden Varianten implizieren die Verwendung von Qt-Konzepten wie Signalen und Slots sowie die Verwendung einer Qt-Ereigniswarteschlange, während eine einfache C++-Implementierung diese Infrastruktur nicht erfordert. Um die Ansätze vergleichbarer zu machen, enthält die Testsuite zwei weitere Testszenarien:

Dadurch ist es möglich, die Auswirkungen der Nutzung von Signalen und Slots einerseits und der Nutzung von QEvents . zu vergleichen andererseits im Vergleich zur einfachen C++-Implementierung, da der Ausführungscode der Zustandsmaschine in allen Fällen identisch ist, aber nur anders verpackt.

Der Testzustandsautomat

Um alle fünf Konkurrenten zu testen, habe ich die in Abb. 1 für das grundlegende Testszenario.


Abbildung 1:Die Testzustandsmaschine, erstellt mit YAKINDU Statechart Tools. (Quelle:Autor)

Die Testzustandsmaschine ist eine einfache flache Zustandsmaschine. Es definiert sechs Staaten A zu F und radelt durch die Staaten. Zwei Eingabeereignisse e1 und e2 definiert, die wechselweise Zustandsübergänge auslösen. Bei einem Zustandsübergang wird auch eine einfache Aktion ausgeführt. Jede Übergangsaktion fügt einfach 10 zu einer Zustandsdiagrammvariablen namens x . hinzu . Der Übergang vom Zustand F zu A erhöht (oder emittiert) zusätzlich das out Veranstaltung o .


Abbildung 2:Die Testzustandsmaschine als SCXML-Modell in Qt Creator. (Quelle:Autor)

Diese Zustandsmaschine wurde mit YAKINDU Statechart Tools definiert, die die Generierung von SCXML unterstützen. Dieses SCXML kann dem Qt-Projekt hinzugefügt und in Qt Creator bearbeitet werden. Wie Sie in Abb. In 2 hat die Zustandsmaschine die identische Struktur wie die in 1 . 1, aber einige Details, wie die Übergangsaktionen, sind in Qt Creator nicht sichtbar. YAKINDU Statechart Tools bieten einige weitere Vorteile, aber ich werde sie hier nicht diskutieren.

Wichtiger ist hier die Tatsache, dass YAKINDU Statechart Tools auch einfache Switch-Case-basierte C++-Zustandsmaschinenklassen generieren können. Es bietet auch eine Option zum Generieren von Qt-fähigen Klassen mit Signalen und Slots, so dass dies praktisch ist. Mit diesem Tool musste ich nur die zustandsmusterbasierte Zustandsmaschine mit QStateMachine implementieren von Hand. Für diese Variante war kein Codegenerator verfügbar. Trotzdem konnte ich mir durch die Verwendung einer einzigen Zustandsdiagrammdefinition viel Implementierungsaufwand sparen und gleichzeitig semantisch äquivalente Zustandsautomaten für die Leistungstests erhalten.

Alle Testfälle folgen demselben Schema. Da ich die durchschnittliche Zeit für die Verarbeitung einzelner Ereignisse messen wollte, erfasste jeder Test eine Million Iterationen einer einzelnen Zustandsschleife. Jede Zustandsschleife führt alle erforderlichen Ereignisse aus, um alle Zustände zu besuchen und alle Übergänge und Übergangsaktionen zu verarbeiten. Eine Zustandsschleife beginnt und endet also mit Zustand A aktiv sein. Dies bedeutet, dass für jeden Testfall 6 Millionen in Ereignisse und Übergangsaktionen und 1 Million out Ereignisse mit ihren zugehörigen Übergangsaktionen werden verarbeitet. Die Tests wurden als Kommandozeilenanwendung ausgeführt und die Zeit für die Iterationen als einzelner Batch aufgezeichnet. Der Zeitverbrauch pro Ereignis lässt sich dann einfach ermitteln, indem man die gemessene Zeit durch die Summe der Anzahl der in . dividiert Events und out Veranstaltungen. Die Tests wurden mehrmals durchgeführt und die Messergebnisse mit den niedrigsten Werten wurden ausgewählt.

Die Tests wurden mit optimiertem Code ohne Debug-Informationen auf meinem alten (Mitte 2014) MacBook Pro mit Core i7 Quad Core CPU 2,4 GHz durchgeführt. Natürlich werden sich die konkreten Zahlen auf verschiedenen Maschinen und Betriebssystemen unterscheiden. Dies ist jedoch nicht relevant, da ich die verschiedenen Implementierungsansätze relativ zueinander vergleichen wollte. Diese relativen Unterschiede sind auf verschiedenen Hardware- und Betriebssystemplattformen vergleichbar.

Schauen wir uns die Leistungszahlen an

Ja – ich denke, fast jeder hätte erwartet, dass eine einfache C++-Implementierung schneller ist als die anderen Alternativen, aber das Ausmaß der Unterschiede ist wirklich erstaunlich.


Abbildung 3:Vergleich der Verarbeitungszeit einzelner Ereignisse. (Quelle:Autor)

Die Verarbeitung einzelner Ereignisse mit reinem C++ dauerte durchschnittlich 7 Nanosekunden. Die Verwendung von SCXML erforderte 33.850 Nanosekunden – das ist ein Faktor von etwa 4.800 und ein riesiger Unterschied! Zum Vergleich:Licht legt mehr als 10 Kilometer zurück, während die SCXML-Zustandsmaschine nur einen einzigen Übergang verarbeitet, während der gleiche Übergang in der einfachen C++-Zustandsmaschine nur so viel Zeit lässt, dass Licht etwas mehr als 2 Meter zurücklegt. Dies impliziert sehr unterschiedliche Größenordnungen für CPU-Zyklen und Energieverbrauch.

Die konkreten Zahlen hängen natürlich von der Maschine und dem verwendeten Prüfverfahren ab. Ich werde dieses Thema später diskutieren. Aber lassen Sie uns zuerst die anderen Zahlen besprechen. Die ersten drei Testszenarien beinhalten alle eine identische Zustandsübergangslogik, die von YAKINDU Statechart Tools generiert wurde, jedoch jeweils auf unterschiedliche Weise verpackt.

Die Verwendung von Signalen und Slots zur Behandlung von Ereignissen dauerte bei Verwendung von Direktverbindungen im Durchschnitt 72 ns. Dieser Mechanismus erfordert also einen minimalen Overhead von ~90%, verglichen mit der tatsächlichen Logik der Zustandsmaschine. An dieser Stelle möchte ich nicht argumentieren, dass die Verwendung von Signalen und Slots Anwendungen langsam macht. Stattdessen würde ich eher behaupten, dass Plain-Code-Implementierungen von Zustandsautomaten extrem schnell sind .

Vergleicht man dies mit dem dritten Szenario, erhält man einen guten Eindruck vom Performance-Overhead, der durch die Verwendung der Event-Queue entsteht. In diesem Szenario werden alle Statechart-Ereignisse durch die Ereigniswarteschlange geleitet. Mit 731 ns pro Ereignis benötigt es einen Faktor von ~10 im Vergleich zu Signalen und Slots und ~100 im Vergleich zu reinem C++.

Wir können davon ausgehen, dass auch für die anderen beiden Szenarien „plain QStateMachine . ein vergleichbarer Overhead gilt “ und „SCXML-Zustandsmaschine“ – beide erfordern eine aktive Ereigniswarteschlange. Wenn also der angenommene Ereigniswarteschlangen-Overhead von den 5200 ns pro Ereignis abgezogen wird, erhalten wir einen ungefähren Zeitverbrauch der QStateMachine Rahmen von 4500ns pro Ereignis. Im Vergleich zum einfachen Code-Ansatz sind QStateMachine-basierte Zustandsautomaten-Implementierungen langsam. Dies ist ein Faktor von etwa 635, verglichen mit der einfachen C++-Codeimplementierung.

Werfen wir zum Schluss noch einen Blick auf den SCXML-Interpreter. Es beinhaltet die Interpretation von JavaScript-Code und fügt einen weiteren Faktor von ~7 hinzu. Im Vergleich zum Plain-Code-Ansatz sind SCXML-basierte State-Machine-Implementierungen sehr langsam.

Was ist mit hierarchischen und orthogonalen Zustandsautomaten?

Bisher habe ich nur ein Profil einer einfachen flachen Zustandsmaschine erstellt. Aber Statecharts bieten viel mehr Funktionen, und die beiden wichtigsten strukturellen Merkmale sind Hierarchie und Orthogonalität. Welche Auswirkungen hat die Verwendung dieser Funktionen in Bezug auf die Laufzeit von Zustandsautomaten?

Um den Einfluss von Hierarchien zu messen, habe ich zunächst eine hierarchische Variante der zu profilierenden Zustandsmaschine definiert, die in Abb. 4.


Abbildung 4:Hierarchisches Testzustandsdiagramm. (Quelle:Autor)

Sie bietet genau das gleiche Verhalten wie die flache Zustandsmaschine, fügt jedoch einige zusammengesetzte Zustände hinzu. Wenn die Funktionalität identisch bleibt, aber nur die Struktur geändert wird, lässt sich herausfinden, wie viel Overhead die Strukturvariante ggf. mit sich bringt.

Zweitens, um den Einfluss der Orthogonalität zu messen, replizierte ich die flache Zustandsmaschine in Form von vier orthogonalen Regionen. Sie alle haben genau die gleiche Funktionalität. Die resultierende Zustandsmaschine (siehe Abb. 5) wird also viermal so viel Arbeit leisten wie die einfache Zustandsmaschine.


Abbildung 5:Orthogonales Testzustandsdiagramm. (Quelle:Autor)

Für die Profilerstellung habe ich die einfache C++- und die SCXML-Implementierung gewählt, da dies die schnellsten und langsamsten Varianten waren. Das Diagramm in Abb. 6 zeigt die Ergebnisse. Es ist sehr ermutigend, dass die Verwendung von Hierarchien in Statecharts in beiden Implementierungsvarianten keine messbaren Auswirkungen auf die Performance hat.


Abbildung 6:Leistungseinfluss von Hierarchien und Orthogonalität. (Quelle:Autor)

Ein weiteres positives Ergebnis ist, dass die Verwendung von Orthogonalität ebenfalls keine negativen Auswirkungen hat. Im Gegenteil, während man hätte erwarten können, dass mindestens die vierfache Verarbeitungszeit die vierfache Arbeit erledigt, sind die effektiven Laufzeitsteigerungen mit den Faktoren ~2,4 und ~3,1 deutlich kleiner als 4.

Warum ist dies der Fall? Der Grund dafür ist, dass es einen allgemeinen Teil der Zustandsmaschinenverarbeitung gibt, der unabhängig von der Verarbeitung einzelner Zustände und Ereignisse ist. Dieser Teil benötigt 52 % (oder 3,5 ns pro Ereignis) für die einfache C++-Zustandsmaschine, verglichen mit 28 % (oder 9300 ns pro Ereignis) für SCXML. Schließlich haben orthogonale Zustände weniger Auswirkungen, wenn generierter C++-Code im Vergleich zu SCXML verwendet wird.

Schlussfolgerung

Einfaches C++ ist bei weitem effizienter als alle Alternativen. Die Verwendung von Signalen und Slots oder der Qt-Ereigniswarteschlange sind Rahmenmechanismen, die die Implementierung und Wartung komplexer Zustandsmaschinenanwendungen erleichtern. Das Qt-State-Machine-Framework erfordert diese beiden Mechanismen. Mit generiertem C++-Code haben Sie die Wahl.

In vielen Szenarien, insbesondere interaktiven, sind sogar SCXML-Zustandsmaschinen schnell genug und bieten möglicherweise mehr Flexibilität, indem sie das Verhalten durch Umschalten der Zustandsdiagrammdefinitionen zur Laufzeit konfigurierbar machen.


Eingebettet

  1. So wählen Sie die beste CAD-Software für Schmuckdesign aus
  2. Die besten CNC-Marken
  3. Wie man die richtige CNC-Maschine auswählt
  4. So stellen Sie die Notfallvorsorge im Lager sicher
  5. Wie überwacht man die Leistung des technischen Personals?
  6. Wie man die beste Bremse für Windkraftanlagen auswählt
  7. So wählen Sie die richtige Kartoniermaschine aus
  8. So wählen Sie die richtige Wasserstrahlschneidemaschine aus
  9. Wie man die beste Blechbiegemaschine auswählt
  10. Wie wählt man die beste Tauchpumpe aus?