Mesures de performance de l'API

Mesures de performance de l'API

La librairie Yoctopuce pour piloter les modules fournit des fonctions quasiment identiques pour tous les langages de programmation, malgré les énormes différences d'environnement (entre C++ et JavaScript, par exemple). Mais tous les langages sont-ils égaux ? Peuvent-ils être utilisés avec des performances similaires ? C'est ce que nous allons tester aujourd'hui, avec quelques surprises à la clef...



Mesure synchrone

Quand on parle de performance, le plus simple est de commencer par regarder le temps que prend une opération bien délimitée, par exemple lire la valeur courante sur un capteur de température Yocto-Meteo. Le capteur mesure bien entendu en permanence la température, mais le logiciel pilote la demande à un instant t et attend la réponse:



Le transport d'informations par USB prend nécessairement quelques millisecondes, par principe: le bus USB transporte les informations par trames, qui circulent sur le bus chaque milliseconde. Lorsqu'on accède à un module à travers une connection TCP distante, le temps de transfert prend facilement plusieurs dizaines de millisecondes par échange.

Pour réduire le temps perdu par le transport, la librairie Yoctopuce maximise la rentabilité des échanges en transportant par avance toutes les données d'un capteur à la fois. Ainsi, si l'on commence par demander la température courante au Yocto-Meteo, il transmet en même temps la valeur min et max, ainsi que la pression et l'humidité. Cela représente un petit surcoût à la première requête, mais représente une grosse économie pour les requêtes suivantes.

Voici les durées typiques pour lire un capteur Yoctopuce:
- en C++ et C#, via USB: 27 [ms] pour le 1er capteur, <1 [ms] pour les suivants
- en C++ et C#, via TCP: 31 [ms] pour le 1er capteur, <1 [ms] pour les suivants
- en JavaScript, via TCP: 35 [ms] pour le 1er capteur, <1 [ms] pour les suivants
- en PHP, via TCP: 34 [ms] pour le 1er capteur, <1 [ms] pour les suivants

Jusque là, rien de très surprenant. Sachant que ce genre de capteur ne change de valeur qu'une à deux fois par seconde, on ne demande rien de mieux.

Commandes

Pour les modules de commande, on demande beaucoup mieux. En effet, pour un contrôleur de moteur ou de lumière, on veut pouvoir varier la commande plus rapidement, et contrôler un nombre important de modules. Par exemple, pour l'exemple du sapin de Noël musical, on avait 24 led à allumer en rythme avec la musique, ce qui exige des performances bien plus élevées. Par chance, le problème est plus simple. Voilà ce qui est nécessaire:



La latence est difficile à mesurer en direct, nous y reviendrons plus tard. Par contre, voilà déjà la durée typique pour envoyer une commande à un module Yoctopuce (le temps occupé dans le programme de contrôle):
- en C++, via USB (Mac OS X, Linux): ~1.5 [ms]
- en C++ et C#, via USB (Windows): <1 [ms]
- en C++, via TCP (Mac OS X, Linux): ~1.5 [ms]
- en C++ et C#, via TCP (Windows): ~2.5 [ms]
- en JavaScript, via TCP: <1 [ms] (identique pour Chrome, Safari, Firefox)
- en PHP, via TCP: <1 [ms]

C'est très peu. Et c'est grâce à cela que le Yocto-Color par exemple peut être facilement utilisé pour faire des petites animations lumineuses.

Evenements

Un autre scénario où les performances comptent est la lecture d'un bouton de contrôle, ou d'une entrée analogique en général. C'est le job du Yocto-Knob par exemple. On pourrait le faire à l'aide de lectures synchrones répétées, comme illustrée précédemment, mais cela exigerait une boucle active très contraignante pour les applications où l'on désire une grande réactivité. La bonne méthode pour cette application consiste à utiliser le système de callbacks, déclenchés spontanément par le module lorsqu'un événement (changement de valeur) se produit:



Cette méthode ne prend quasiment pas de temps au programme de contrôle (< 1ms dans tous les cas) puisque tout est fait en amont dans le module Yoctopuce. Par contre, comme pour le cas précédent d'une commande, il se pose la question de la latence: combien de temps se passe-t-il entre l'événement réel et l'appel du callback dans le programme de contrôle ? Arrive-t-on dans tous les langages à limiter la latence de sorte à donner l'impression de la simultanéité ?

Mesure de la latence

La difficulté pour mesurer la latence est que le début et la fin de l'intervalle à mesurer ne se passent pas au même endroit. Il n'y a donc pas de manière simple pour mesurer la durée. Par contre, on peut facilement créer un petit circuit de test avec deux modules qui additionne les deux latences que nous cherchons à mesurer, tout en commençant et se terminant dans le programme de contrôle (donc est mesurable). Pour ce faire nous allons simplement brancher un Yocto-Relay sur un Yocto-Knob:

Notre banc de test: un Yocto-Knob relié a un Yocto-Relay
Notre banc de test: un Yocto-Knob relié a un Yocto-Relay



Le scénario est très simple: le programme lance le chronomètre, donne l'ordre au relais de changer de position, et attend de recevoir de la part du Yocto-Knob l'événement correspondant au contact correspondant qui s'est fermé/ouvert. La durée mesurée entre la commande et la réception de l'événement inclut donc:
- le temps nécessaire à lancer la commande (env. 1 [ms])
- la latence de l'exécution de la commande (à déterminer)
- le temps de commutation de relais (env. 3ms)
- la latence pour le retour de l'événement vers le callback (à déterminer)



Si la latence totale dépasse 100ms, elle est perceptible comme un décalage à l'être humain, et peut aussi poser des problèmes pour les applications où il faut réagir instantanément. A titre de comparaison, voici les latence de quelques applications classiques (transmission aller-simple):
- latence d'une souris PS/2: 25 [ms]
- latence d'une souris USB: 8 [ms]
- latence d'un avion radio-commandé: 30 [ms]

Et les résultats des mesures de latence cumulée pour un aller-retour à travers les deux modules Yoctopuce:
- en C++, via USB (Mac OS X, Linux): 7.5 [ms]
- en C++ et C#, via USB (Windows): 6 [ms]
- en C++, via TCP (Mac OS X, Linux): 16 [ms]
- en C++ et C# via TCP (Windows): 18 [ms]
- en JavaScript, via TCP: 20 [ms] (identique pour Chrome, Safari, Firefox)
- en PHP, via TCP: 13 [ms]

Sachant que ces temps incluent deux fois la latence, en plus du temps de commutation du relais, cela démontre que les temps de latence des modules Yoctopuce sont de l'ordre de quelque millisecondes, loins d'être perceptibles par un humain.

La bonne surprise est que cela reste vrai lorsque les modules ne sont pas accédé directement en USB mais via un tunnel TCP. Même en JavaScript, qui n'est pourtant pas réputé comme un langage performant, vous pouvez interfacer des modules Yoctopuce pour réagir à l'environnement en quelques millisecondes! Qui l'eu cru...


Notes:
- les mesures C++ et C# sous Windows ont été effectuées sur un Lenovo X301 (Core 2 duo).
- les mesures C++ sur Linux ont été effectuées sur un portable ASUS (Core i3).
- les mesures C++ sur Mac OS X ont été effectuées sur un iMac de 2010 (Core i7).
- les mesurer en Javascript et PHP on été effectuées sur le même iMac
- le code utilisé pour ces tests est disponible ici

Commenter 3 commentaires Retour au blog



1 - ab00 Mardi 28 février 2012 11H53

Pourquoi pas de mesure avec l'interface PHP?

2 - mvuilleu (Yocto-Team)Mardi 28 février 2012 12H38

Il n'y a pas encore de mesures en PHP parce que c'est le dernier langage pour lequel nous n'avons pas encore terminé l'implémentation des callbacks dans l'API. Il sera disponible dans la mise à jour des librairies prévue pour cette semaine.

Pour PHP, il faudra bien sur différencier les deux scénarios d'utilisation: en temps que langage de scripting, comme utilisé par exemple pour notre station meteo, et en temps que générateur de contenu sur un site web. Dans le premier cas, l'utilisation sera identique aux autres langages, et les performances devraient être similaires. Pour l'utilisation sur un site web, le plus efficace sera probablement de recevoir les callbacks depuis un iframe de contrôle séparé, ce qui rajoutera une petite latence supplémentaire, mais devrait rester très intéressant. A suivre...

3 - mvuilleu (Yocto-Team)Mercredi 29 février 2012 18H19

Voilà, les stats PHP ont été ajoutées: elles sont même meilleures que JavaScript !
Comme PHP n'est pas multithreadé, il s'agit de mesures faites à l'intérieur d'un seul thread (une seule page par exemple, ou un programme PHP en ligne de commande).

On fera un de ces jours un post pour expliquer comment utiliser un iframe pour créer un environnement de callback multithreadé en PHP...

Yoctopuce, get your stuff connected.