Helios: 299 792 458 m/s
Par Yomgui le lundi 2 mars 2009, 11:35 - Autres projets - Lien permanent
Avant la petite gastro de ce w.e. (ouai... encore!) j'ai fais quelques tests
de vitesses avec le nouveau design d'Helios.
En voilà le récit...
Décidément en ce moment j'arrête plus de poster sur mon blog... si cela continue on va dire que je suis un blogueur (et pas un blagueur comme me le propose le correcteur orthographique).
Donc je disais que j'ai fais un petit essai de mesure de vitesse de lecture avec Helios. Les conditions de tests étaient:
- 2 Pegasos (I et II) reliés avec un câble IEEE-1394 (6-6) formant l'ensemble du réseau.
- Helios tournant sur le Peg2, le Peg1 étant l'unité lue.
- Utilisation du module Python.
Dans ce dernier j'ai utilisé la propriété timer d'un objet Device qui retourne le 3-tuple correspondant au temps du bus (secondes, nombre de cycles (8000/s), offset du cycle (horloge de 24.576MHz modulo 3072)).
J'ai donc encadré par 2 appels à cette propriété la méthode Read de
Device pour lire un bloque de 10 Méga-Octets en mémoire du Peg1,
depuis le Peg2. Cette méthode provoque des transactions asynchrone de type
lecture blocs sur le bus. Une boucle for permettant de
découper la taille demandée en morceaux d'une taille limitée par la contenance
maximale d'un paquet asynchrone IEEE1394.
Tout ceci rajoute évidement un plus dans le temps par-rapport à un appel C
direct, mais les résultats montrerons que l'impacte est minime.
Les 2 Pegs ont un mode vitesse maximal de S400 et en asynchrone à cette vitesse un paquet peut transporter jusqu'à 2048 octets. J'ai donc obtenu une vitesse d'environs 9Mo/s... Il faut savoir qu'en premier je n'avais pas utilisé la vitesse S400, mais S100, qui donne un résultat de 5Mo/s!
Par la suite j'ai modifié le code C du module python pour encadrer l'appel à
la fonction Helios Helios_DoAsyncTransaction() qui s'occupe
d'initialiser, d'envoyer et d'attendre (Wait()) la réponse d'une
transaction asynchrone quelconque. J'ai utilisé ici ReadEClock()
de timer.device. Cela permet d'éliminer le rajout de temps par les
appels à travers les couches Python.
Un résultat approchant les 10Mo/s montrant 2 choses: 2 timers
hardware différents donnant des résultats cohérents validant les vitesses et le
code python bien optimisé pour ne pas rajouter trop de temps.
Mais revenons aux résultats même: voici les vitesses maximales en Méga-bits/s (Mb/s) pour chaque vitesses standards possibles sur un bus IEEE1394a:
- S100 : 98304000 Mb/s
- S200 : 196608000 Mb/s
- S400 : 393216000 Mb/s
On en déduit donc une taille maximale de données transportées par paquets (la bande passante par paquet donc):
- S100 : 1536 octets
- S200 : 3072 octets
- S400 : 6144 octets
De notre résultat d'environs 5 Mo/s en S100 en asynchrone on en retient donc
que 50% de la bande passante est utilisée.
Et de notre résultat d'environs 10 Mo/s en S400 en asynchrone: c'est 20% de la
bande passante qui est utilisée.
Le résultat en S400 n'est pas très bon... Bon pour l'instant Helios n'est pas en phase d'optimisation et la création du paquet, sa mise en place la liste de lecture de la DMA du chipset OHCI1394, ainsi que l'attente du résultat rajoute pas mal de temps supplémentaire. Mais surtout il faut voir que je n'utilise pas ici la DMA de la meilleure façon qu'il soit: je pose un descripteur pour mon paquet, puis j'attends le résultat. C'est très simple, mais très mauvais pour les performances!
Effectivement il faut savoir que la DMA se programme. On lui donne une liste de descripteurs de paquets à envoyer et on attend les résultats après. Ainsi les paquets asynchrones peuvent être envoyés au plus vite, sans attendre les réponses les unes après les autres. Ce qui est le principe même du mode asynchrones après tout.
Helios possède en l'état un bloc mémoire de 64Ko pour créer la liste de
descripteurs en envois et un descripteur fait 64 octets (il faut 1 descripteur
pour 1 paquet, tout types confondus). On peut donc préparer 1024 paquets à
l'avance (bon en faite c'est pas possible à l'avance, dès que le bus est prêt
on envoi, les descripteurs suivants s'attacheront à la suite). En S400, cela
implique 2Mo de données qui peuvent être ainsi mis en tampons pour
l'envoi.
Donc vous le voyez je perd beaucoup de temps pour rien à attendre la réponse
d'un paquet pour envoyer le prochain!
Mais attention, il y a une limite aussi en réception où j'utilise ici seulement 2 descripteurs en mode remplissage continue : quand l'un est plein je le retire de la liste, j'utilise le premier pour la suite et j'ajoute un nouveau à la suite de celui-ci pour garantir les cas de dépassement, et c'est repartis pour un tour... Mais si jamais un paquet arrive et qu'il n'y a pas de descripteur pour le recevoir ce paquet est annulé et il doit être recommencé.
Donc il faudra que je refasse le module Python pour qu'il puisse remplir au
plus vite la DMA et quand même temps il récupère les réponses. Cela me fait
dire que la fonction Helios_DoAsyncTransaction() est peut-être
très sympathique pour le développeur mais très dangereuse pour les
performances!
Notes: en tout cas avec une optimisation nulle on a déjà 10 fois mieux
que l'USB 1.x et je crois aussi bien que l'USB 2.0! ... en asynchrone!

EDIT 2009/03/05
Hier soir (enfin plutôt ce matin...), en modifiant largement le code du module Python plus quelques optimisations dans le code d'Helios, j'ai atteint la barre des 20Mo/s !!! Soit dans les 18Mo/s de façon stable (si vous n'avez pas trop de processus qui viennent embêter). Pour info, le seul port filaire sur le Peg2 plus rapide est le Giga-Ethernet (20Mo/s = 160Mb/s).
Commentaires
Il est fou Afflelou ! Et Yomgui aussi
Que d'excellentes news ces temps