Maximiser la performance des machines d'états Qt
Si vous développez des applications avec Qt et employez des machines d'états, vous utilisez probablement le framework de machine d'états de Qt. Vous pouvez définir la machine en C++ pur ou SCXML, ou générer du code C++ depuis diagrammes state-chart. Cet article évalue ces options en termes de fonctionnalité, applicabilité, et performance.
Comme développeur logiciel, vous avez probablement écrit d'innombrables blocs switch-case. Pour beaucoup, c'est la façon la plus simple d'implémenter des machines d'états quand aucun outil haut niveau n'est dispo. Cependant, comme la complexité grandit, la maintenance devient un défi, et la logique switch-case artisanale n'est plus soutenable.
Implémenter des machines d'états
Quand on travaille avec un langage orienté objet comme C++, le pattern d'état est une stratégie éprouvée. Il modélise chaque état comme une classe, et transitions comme classes optionnelles séparées. Une machine d'états est construite en créant instances de ces classes et les câblant ensemble. Un framework peut réduire significativement le boilerplate.
L'API machine d'états de Qt laisse vous configurer une machine avec code concis, laissant les sémantiques d'exécution au framework. Bien que cela simplifie l'implémentation, le code source peut encore grossir quand une machine contient des dizaines ou centaines d'états, rendant le design global difficile à saisir.
Qt supporte aussi State Chart XML (SCXML), le standard W3C pour descriptions de state-chart. Écrire XML à la main est fastidieux, donc Qt Creator offre un éditeur graphique simple.
Indépendamment de la technique d'implémentation, un modèle graphique est la façon la plus efficace d'éditer et comprendre une machine d'états. Modèles graphiques peuvent être exprimés en texte via SCXML, ou utilisés pour générer code source en tout langage, incluant switch-case C++ ou construction QStateMachine. En déléguant la transformation à un outil, vous évitez la douleur du code manuel et amenez toutes approches au même niveau d'utilisabilité. Les implémentations restent cependant fondamentalement distinctes. Cet article focus sur le comportement runtime, surtout performance.

Unsplash Photo by Austris Augusts on Unsplash
Les concurrents
Comment les approches diffèrent-elles en usage CPU ? Pour répondre, j'ai créé une suite de benchmarks. La première partie compare les différentes stratégies d'implémentation. Voici les concurrents :
- Interpréteur SCXML — la machine test est définie en SCXML et exécutée par la classe QSCXMLStateMachine de Qt.
- Pattern d'état — la machine test est implémentée usando classes QStateMachine.
- Code C++ pur — la machine test est implémentée par une classe C++ utilisant approche switch-case basique.
Note : Code pour ces exemples peut être trouvé ici.
Les deux premières variantes impliquent l'usage de concepts Qt comme signaux/slots et file d'événements Qt, tandis qu'une implémentation C++ pur ne requiert pas cette infrastructure. Pour rendre les approches plus comparables, la suite de test inclut deux scénarios additionnels :
- Code C++ pur avec signaux et slots — même implémentation que ci-dessus mais câblée dans le système signal/slot de Qt.
- Code C++ pur avec QEvent — l'approche C++ pur qui route les événements through la file d'événements Qt.
Cela permet d'isoler l'impact des signaux/slots et file d'événements sur le temps d'exécution tout en gardant la logique de transition d'état identique.
La machine d'états de test
Pour les cinq variantes, j'ai utilisé la machine d'états illustrée Figure 1 pour le scénario de test basique.

Figure 1 : La machine d'états de test, créée avec YAKINDU Statechart Tools. (Source : Auteur)
La machine a six états plats A-F, cycle through eux, et réagit à deux événements d'entrée e1 et e2 qui déclenchent alternativement transitions. Chaque action de transition ajoute 10 à une variable x ; la transition de F vers A émet aussi l'événement de sortie o.

Figure 2 : La machine d'états de test comme modèle SCXML dans Qt Creator. (Source : Auteur)
J'ai défini la machine avec YAKINDU Statechart Tools, qui peut générer à la fois SCXML et code C++ switch-case pur. Le code généré supporte aussi signaux/slots Qt. J'ai implémenté la variante QStateMachine manuellement, mais toutes autres implémentations découlent de la même définition state-chart.
Chaque test a exécuté un million d'itérations d'une boucle d'état unique. Une boucle commence et finit avec état A actif, et traite 6 millions d'événements d'entrée, 1 million d'événements de sortie, et leurs actions de transition associées. Les tests ont été exécutés comme application command-line, et le temps pour le batch entier enregistré. Le temps par événement déduit en divisant temps total par nombre d'événements traités. Les valeurs les plus basses de runs répétés utilisées.
Benchmarks effectués sur MacBook Pro 2014 avec 2,4 GHz quad-core i7 en build release optimisé. Nombres exacts varieront sur autre hardware, mais différences relatives restent consistantes.
Chiffres de performance
Traiter un événement unique avec C++ pur a pris en moyenne 7 ns, tandis que SCXML a requis 33 850 ns — facteur ~4 800. L'usage de signaux/slots ajoutait ~72 ns par événement (~90 % overhead). Le routage via file d'événements Qt ajoutait ~731 ns par événement (~10x signaux, ~100x C++ pur). Après soustraction overhead file, le framework QStateMachine coûte ~4 500 ns par événement — ~635x plus lent que C++ pur. L'interpréteur SCXML, qui interprète JavaScript, ajoute un autre facteur ~7x, le rendant la variante la plus lente.

Figure 3 : Temps traitement événement unique comparé. (Source : Auteur)
Impact de hiérarchie et orthogonalité
J'ai aussi profilé variantes hiérarchiques et orthogonales de la même machine. La machine hiérarchique (Figure 4) et la machine orthogonale (Figure 5) ont comportement identique mais structure différente.

Figure 4 : Statechart test hiérarchique. (Source : Auteur)

Figure 5 : Statechart test orthogonal. (Source : Auteur)
Résultats, Figure 6, indiquent qu'ajouter hiérarchie n'introduit pas overhead mesurable dans aucune implémentation. L'orthogonalité ne double pas non plus le runtime comme on pourrait s'attendre ; l'augmentation est ~2,4x pour C++ pur et ~3,1x pour SCXML, bien en dessous du coût théorique quadruple.

Figure 6 : Impact performance hiérarchies et orthogonalité. (Source : Auteur)
Conclusion
C++ pur délivre la meilleure performance. Signaux, slots, et file d'événements Qt traduisent vitesse pour commodité et maintenabilité, tandis que le framework QStateMachine est le plus lent des trois variantes core. SCXML est souvent assez rapide pour applications interactives et offre l'avantage de reconfiguration runtime. Quand la vitesse maximale est critique, le code switch-case artisanale est l'approche recommandée.
Embarqué
- Choisir le Bon Logiciel CAO pour la Conception de Bijoux : Guide Complet
- Meilleures marques de machines CNC pour la précision et la fiabilité
- Choisir la machine CNC idéale : guide pratique
- Maîtriser la préparation aux urgences en entrepôt : guide complet
- Techniques efficaces pour surveiller la performance du personnel technique
- Choisir le frein éolienne idéal pour performance fiable
- Guide expert : choisir la machine d'encartonnage idéale pour votre ligne de production
- Choisir la machine de découpe waterjet idéale pour votre atelier
- Choisir la Bonne Plieuse à Métaux : Guide Complet