title: Latence et boucle de rétroaction
url: http://mathieu.agopian.info/blog/latence-et-boucle-de-retroaction.html
hash_url: f1ed7d7dff2ed14214bdf7a25dde1d74

<h2>Latence</h2>
<p>La <a class="reference external" href="http://fr.wikipedia.org/wiki/Latence_%28informatique%29">latence</a> en informatique est un délai minimum de transmission. C'est un des
principaux ennemis de la performance notamment dans le domaine du web.</p>
<p>Un site web est généralement composé de multiples fichiers statiques (css,
javascript, images et icônes en tout genre). Pour afficher la totalité d'une
page, il faut donc faire de multiples requêtes au serveur, chacune prenant un
certain temps de traitement, à quoi on rajoute la latence (le temps de
transfert sur le réseau).</p>
<p>On imagine facilement l'impact d'une forte latence lorsqu'il faut effectuer
plusieurs dizaines voire centaines de requêtes. Même si la latence n'est que de
10ms, si on multiple ça par 100, on atteint déjà une seconde.</p>
<p>Prenons un autre exemple&nbsp;: il n'est pas rare d'avoir des pages nécessitant des
dizaines (des centaines, voire des milliers&nbsp;?) de requêtes SQL à une base de
données. Là encore, il faut multiplier ce nombre de requête par le temps de
traitement par la base de données, mais aussi par la latence.</p>
<p>Étant donné la difficulté de réduire la latence, l'optimisation de la
performance passe par le réduction du nombre de requêtes&nbsp;: on fait des
<em>bundles</em> pour les fichiers statiques (on regroupe toutes les CSS ou les
fichiers javascript, on crée des <em>image map</em> pour les icônes), on fait usage du
<em>JOIN</em> pour les requêtes SQL...</p>
</div>
<div class="section" id="boucle-de-retroaction">
<h2>Boucle de rétroaction</h2>
<p>La <a class="reference external" href="http://fr.wikipedia.org/wiki/Boucle_de_r%C3%A9troaction">boucle de rétroaction</a> (appelée «&nbsp;feedback loop&nbsp;» en anglais) permet de
raffiner un système afin d'arriver à un équilibre, à un objectif.</p>
<p>Une boucle de rétroaction basée sur des mesures de position permettra à robot
d'atteindre la position souhaitée&nbsp;: la vitesse et la direction seront adaptées
en fonction de la distance à l'objectif.</p>
<p>Plus la boucle de rétroaction sera longue, plus l'équilibre sera long a
atteindre. En effet, si la mesure de position ne se fait qu'une fois toutes les
10 secondes, soit le robot devra se déplacer très lentement, soit faire de
nombreux aller-retours.</p>
</div>
<div class="section" id="le-rapport">
<h2>Le rapport&nbsp;?</h2>
<p>En tant qu'informaticien, il nous arrive régulièrement d'avoir à raffiner un
bout de code en fonction de différents paramètres&nbsp;: l'expression du besoin, la
rapidité d'exécution, l'occupation mémoire...</p>
<p>Et ce raffinage se fait par le biais d'une boucle de rétroaction qui peut
prendre différentes formes&nbsp;:</p>
<ul class="simple">
<li>des tests automatisés (test unitaires ou fonctionnels, TDD...)</li>
<li>des tests manuels</li>
<li>des simulations</li>
<li>des aller-retours avec l'utilisateur final, le décideur, ...</li>
</ul>
<p>Si la latence s'invite dans ce mécanisme, on se retrouve dans la même situation
que le robot qui doit atteindre une position, mais qui n'a de retour sur sa
position que rarement. Soit on avance vite et on risque les aller-retours
(comprendre&nbsp;: réécriture du code), soit on avance lentement.</p>
<p>Dans tous les cas, c'est un cauchemar.</p>
<p>Voici quelques exemples de latence&nbsp;:</p>
<ul class="simple">
<li>besoin mal exprimé ou mal compris (la mesure de position n'est pas fiable)</li>
<li>périmètre fonctionnel qui change (la position finale du robot change en cours
de route)</li>
<li>grand nombre d'aller-retours avec l'utilisateur final/décideur (nécessité de
faire un très grand nombre de mesures de position)</li>
<li>retours de l'utilisateur final/décideur très lents (mesure de position très
rare)</li>
<li>dialogue avec une API/base de données/système distant/... très lent (chaque
mesure de position demande de très longs traitements)</li>
<li>peu de confiance dans les résultats (nécessité de refaire plusieurs fois les
mesures ou de les retraiter)</li>
</ul>
</div>
<div class="section" id="un-exemple-concret">
<h2>Un exemple concret</h2>
<p>Avec mon collègue <a class="reference external" href="https://larlet.fr/david/">David</a> nous nous sommes chargés de la résolution d'un ticket
pour le projet <a class="reference external" href="https://addons.mozilla.org">AMO</a>.</p>
<p>Afin de pouvoir présenter des graphiques d'utilisation/téléchargement des
extensions à leur auteur (comme pour <a class="reference external" href="https://addons.mozilla.org/en-US/firefox/addon/firebug/statistics/?last=30">Firebug</a>), toutes les requêtes de
téléchargement et de demande de mise à jour sont enregistrées et stockées dans
une base de données «&nbsp;big data&nbsp;» (pour les curieux&nbsp;: c'est stocké dans <a class="reference external" href="http://hadoop.apache.org/">Hadoop</a>
et récupéré par le biais de <a class="reference external" href="https://hive.apache.org/">Hive</a>). On parle de plus d'un milliard de requête
par jour, toutes requêtes confondues.</p>
<p>Ce qui nous a d'abord paru simple et rapide à implémenter, s'est transformé en
deux semaines de sprint (et n'est pas encore terminé).</p>
<p>Nous avons subit et fait partie des différentes formes de latences listées
ci-dessus&nbsp;:</p>
<ul class="simple">
<li>besoin mal exprimé ou mal compris&nbsp;: la répartition des tâches entre nous et
notre interlocuteur a changé plusieurs fois. Par ailleurs nous n'avions
aucune connaissance du système avant de nous y attaquer.</li>
<li>périmètre fonctionnel qui change&nbsp;: malheureusement, arrivé au bout de la
première semaine de sprint, nous avons appris qu'il nous fallait refaire la
moitié de notre travail différemment suite à des contraintes non prévues.</li>
<li>grand nombre d'aller-retours avec l'utilisateur final/décideur&nbsp;: à l'heure de
l'écriture de ce billet, nous en sommes à 54 commentaires sur le ticket.</li>
<li>retours de l'utilisateur final/décideur très lent&nbsp;: nous travaillons en
France, et notre interlocuteur sur la côte ouest des USA (-9h). Il est
fréquent d'avoir besoin d'attendre le lendemain pour avoir une réponse, dans
un sens ou dans l'autre.</li>
<li>dialogue avec un système distant très lent&nbsp;: de par le nombre de données à
traiter, chaque requête à Hive (au nombre de 6) prend en moyenne 15 minutes,
et la taille des données à télécharger varie entre 500Mo et 1.6Go.</li>
<li>peu de confiance dans les résultats&nbsp;: nous essayions de mettre au point les
requêtes Hive à exécuter, et par le même temps, le post-traitement de ces
requêtes, pour coller au plus proche aux statistiques et graphiques attendus.
Jouer sur deux paramètres en même temps est déjà malaisé en temps normal,
mais là il nous était de plus très laborieux de confronter nos résultats avec
ceux de la production.</li>
</ul>
</div>
<div class="section" id="la-solution">
<h2>La solution</h2>
<p>Diminuer la latence dans la boucle de rétroaction, par tous les moyens
possibles.</p>
<p>De la même manière qu'il arrive régulièrement de lancer un interpréteur Python
(ou un <em>ipdb</em> ;) pour bidouiller et expérimenter avec un petit bout de code
et avoir des retours immédiats, il faut tout faire pour accélérer la boucle de
rétroaction.</p>
<ul class="simple">
<li>ça paraît évident, mais connaître précisément le besoin et le contexte avant
de se lancer est primordial. Nous avons facilement perdu deux à trois jours à
cause de ça, en début de sprint, avec notre envie d'avancer rapidement (et de
passer à quelque chose de plus fun ;)</li>
<li>périmètre fonctionnel qui change&nbsp;: difficile de le prévoir ou de l'éviter,
mais je pense qu'en ayant une boucle de rétroaction plus courte, nous aurions
eu des résultats plus rapidement, et aurions alors plus rapidement rencontré
les contraintes qui ont fait changer le périmètre.</li>
<li>le nombre d'aller-retours avec l'interlocuteur, et leur lenteur&nbsp;: quand nous
avons pu mettre en place un appel vidéo journalier, beaucoup de choses se
sont débloquées.</li>
<li>dialogue avec le système distant très lent&nbsp;: nous aurions dû <a class="reference external" href="http://www.voidspace.org.uk/python/mock/">mock</a> beaucoup
plus tôt le retour des requêtes Hive. Nous aurions ensuite dû récupérer un
jeu de donnée complet (plusieurs Go) le plus tôt possible pour faire des
tests sur le post-traitement dans un premier temps, puis ensuite seulement
essayer d'améliorer les requêtes Hive. Faire les deux en même temps est une
erreur qui nous a coûté de nombreux appels à Hive (et donc de nombreuses
attentes et rétro-pédalages).</li>
<li>peu de confiance dans les résultats&nbsp;: encore une fois vu la taille des
données à traiter, il nous était très difficile de confronter nos données à
celles attendues (uniquement disponibles en production). Nous avons fini par
mettre en production un post-traitement parallèle à l'actuel, et stocker les
données dans d'autres tables, en attendant d'avoir la version finale et
raffinée de l'algorithme et des requêtes.</li>
</ul>
<p>Et à ma plus grande honte, ne pas avoir de tests unitaires a été un boulet
supplémentaire&nbsp;: lancer un post-traitement pour s'apercevoir 15 minutes plus
tard qu'on a oublié une virgule dans le code...</p>
</div>
