diff --git a/figures/moodtvperf.dot b/figures/moodtvperf.dot new file mode 100644 index 0000000..988f4eb --- /dev/null +++ b/figures/moodtvperf.dot @@ -0,0 +1,16 @@ +digraph { + rankdir=LR; + mood [shape="tab",label="Mood TV"] + perf [shape="component",label="Collecteur de performances"] + { + rank=same + stat [shape="component",label="Analyseur des performances"] + db [shape="folder",label="Mesures"] + + db -> stat + } + + mood -> perf + perf -> db + db -> perf +} diff --git a/figures/perf1.tex b/figures/perf1.tex new file mode 100644 index 0000000..af70e6c --- /dev/null +++ b/figures/perf1.tex @@ -0,0 +1,11 @@ +\begin{figure}[H] + \begin{center} + \begin{tabular}{r | c c l } + hydration & $2918.55$ & $\pm 38.05$ & ms \\ + mount & $3551.64$ & $\pm 45.83$ & ms \\ + ready & $19022.34$ & $\pm 3545.82$ & ms + \end{tabular} + \end{center} + \caption{Performances initiales} + \label{perf1} +\end{figure} diff --git a/figures/perf2.tex b/figures/perf2.tex new file mode 100644 index 0000000..f956d7a --- /dev/null +++ b/figures/perf2.tex @@ -0,0 +1,11 @@ +\begin{figure}[H] + \begin{center} + \begin{tabular}{r | c c l | r r l} + hydration & $3060.55$ & $\pm 67.92$ & ms & augmentation & $2918.55$ & $\rightarrow 3060.55$ \\ + mount & $3767.47$ & $\pm 73.72$ & ms & augmentation & $3551.64$ & $\rightarrow 3767.47$ \\ + ready & $7198.81$ & $\pm 819.59$ & ms & diminution & $19022.34$ & $\rightarrow 7198.81$ + \end{tabular} + \end{center} + \caption{Performances après avoir dédupliqué les requêtes} + \label{perf2} +\end{figure} diff --git a/figures/perf3.tex b/figures/perf3.tex new file mode 100644 index 0000000..8b81cee --- /dev/null +++ b/figures/perf3.tex @@ -0,0 +1,11 @@ +\begin{figure}[H] + \begin{center} + \begin{tabular}{r | c c l | r r l} + hydration & $2437.44$ & $\pm 35.65$ & ms & diminution & $3060.55$ & $\rightarrow 2437.44$ \\ + mount & $3033.36$ & $\pm 46.56$ & ms & diminution & $3767.47$ & $\rightarrow 3033.36$ \\ + ready & $6517.79$ & $\pm 521.00$ & ms & faible diminution & $7198.81$ & $\rightarrow 6517.79$ + \end{tabular} + \end{center} + \caption{Performances après avoir dédupliqué les requêtes} + \label{perf3} +\end{figure} diff --git a/figures/ttest.tex b/figures/ttest.tex new file mode 100644 index 0000000..dd280be --- /dev/null +++ b/figures/ttest.tex @@ -0,0 +1,15 @@ +\begin{figure} + % http://homework.uoregon.edu/pub/class/es202/ztest.html + % https://en.wikipedia.org/wiki/Standard_score + \begin{align*} + X &\hookrightarrow \mathcal{N}(\mu_X, \sigma_X^2) \\ + Y &\hookrightarrow \mathcal{N}(\mu_Y, \sigma_Y^2) \\ + s_X &= \frac{\sigma^{2\star}_X}{n_X} \\ + s_Y &= \frac{\sigma^{2\star}_Y}{n_Y} \\ + T &= \frac{\mu^{\star}_X - \mu^{\star}_Y}{\sqrt{s_X + s_Y}} \\ + f &= \frac{(s_X + s_Y)^2}{\frac{s_X^2}{n_X-1}+\frac{s_Y^2}{n_Y-1}} \\ + \text{Si $\mu_X = \mu_Y$, alors} &\; T \hookrightarrow t(0; f) + \end{align*} + \caption{Calcul du t-test} + \label{ttest} +\end{figure} diff --git a/images/mood-tv-after.png b/images/mood-tv-after.png new file mode 100644 index 0000000..4b46772 Binary files /dev/null and b/images/mood-tv-after.png differ diff --git a/images/mood-tv-before.png b/images/mood-tv-before.png new file mode 100644 index 0000000..d5f0b14 Binary files /dev/null and b/images/mood-tv-before.png differ diff --git a/references.bib b/references.bib index cff55e8..9194135 100644 --- a/references.bib +++ b/references.bib @@ -190,3 +190,18 @@ howpublished="\url{https://developer.mozilla.org/en-US/docs/Web/HTML/Content_categories}", note="[En ligne; accédé le 06 Février 2023]" } + +@misc{hydration, + title={hydrate (API Reference)}, + author={React}, + howpublished="\url{https://beta.reactjs.org/reference/react-dom/hydrate}", + note="[En ligne; accédé le 07 Février 2023]" +} + +@misc{performanceapi, + title={Performance}, + author={Mozilla Developer Network}, + howpublished="\url{https://developer.mozilla.org/en-US/docs/Web/API/Performance}", + note="[En ligne; accédé le 07 Février 2023]" + +} diff --git a/report.tex b/report.tex index 871afc8..aa67133 100644 --- a/report.tex +++ b/report.tex @@ -5,7 +5,9 @@ \usepackage[T1]{fontenc} \usepackage[a4paper, margin=0.7in]{geometry} \usepackage[french]{babel} +\usepackage{amsmath} \usepackage{amsfonts} +\usepackage{mathtools} \usepackage{mathabx} \usepackage{listings} \usepackage{xcolor} @@ -501,7 +503,7 @@ Ce système prend la forme de plusieurs librairies, qui doit remplir les conditi \item Il doit être facilement réutilisable dans d'autres projets. \item Il doit permettre de changer de fournisseur de service pour l'envoi d'emails sans devoir faire de grands changements du côté des utilisateurs de la librairie. \item Il doit pouvoir envoyer des emails qui s'affichent de la manière voulue sur la majorités des boîtes mail. - \item Il doit pouvoir envoyer des emails contenant des informations sur l'utilisateur. + \item Il doit pouvoir envoyer des emails contenant des informations sur l'utilisateur; cette étape devra être faite avec le service de \term{templates} d'email sur \entity{Amazon SES} \cite{amazonses} \cite{amazonsestemplate} \item Il doit permettre de décrire le contenu des emails d'une manière facilement maintenable et réutilisable. \end{itemize} @@ -607,10 +609,10 @@ J'ai choisi la librairie \entity{MJML} \cite{mjml}, qui utilise un langage resse \subsection{Rétrospective sur le système d'envoi de mails} -% - [ ] parler des désavantages de cette méthode -% - [ ] parler de ce qu'on prévoyait de faire -% - [ ] parler de comment on l'a fait (pour showcase l'avantage du monorepo), et des étapes prises -% - [ ] parler des améliorations futures qui pourront être faites +% - [x] parler des désavantages de cette méthode +% - [x] parler de ce qu'on prévoyait de faire +% - [~] parler de comment on l'a fait (pour showcase l'avantage du monorepo), et des étapes prises +% - [x] parler des améliorations futures qui pourront être faites % (ajouter plus de providers, simplifier la logique pour choisir les providers, react.email, svelte, etc.) % - [ ] Comparer la maintenabilité @@ -636,7 +638,7 @@ Malgré \entity{MJML} et le système de composants, écrire des templates d'emai \item Le choix de faire le rendu final des templates via les templates d'\entity{Amazon SES} rend l'écriture des emails plus compliquée (il faut mixer ensemble deux différents formats handlebars, l'un étant interprété de notre côté, et l'autre étant interprété du côté d'\entityb{Amazon SES} \figref{emailsrchbs}). \\ - Ce choix rend aussi le débuggage de toute erreur lors du deuxième rendu très compliqué, + Ce choix rend aussi le débogage de toute erreur lors du deuxième rendu très compliqué, car le seul moyen pour accéder aux erreurs d'envoi de mails sur \entityb{Amason SES} est de mettre en place un système de transfert de messages, puis de l'activer en envoyant les mails avec un paramètre précis. \end{itemize} @@ -689,21 +691,129 @@ je pense qu'il est encore possible d'améliorer de différentes manières ce sys \subsection{Introduction?} +\entity{Mood TV} est une application tournant sur les télévisions \og intelligentes \fg produites par \entity{LG}. +Ces télévisions font tourner le système d'exploitation \entity{WebOS}, et la version pour laquelle nous visons l'application utilise \entity{Chrome 53}. + +Comme ces télévisions utilisent chrome, notre application peut être programmée entièrement en React, +à condition de s'assurer que le code javascript soit compatible avec cette version de chrome. + +Mon travail sur \entity{Mood TV} a été d'optimiser le temps de chargement de la page: +avant, celle-ci prenait environ 10 secondes à charger, +et j'ai été laissé en autonomie pour trouver pourquoi le temps de chargement est si haut et comment le baisser. + % Présentation de la plateforme, des télés (chrome 53 hehe), mentionner procentric % Parler de l'hydration, du rendu, etc. \subsection{Analyse des performances} +La première étape avant de pouvoir optimiser les performances de l'application est de mesurer ses performances. +Il faut pour celà mesurer de manière précise le temps moyen de chargement, +et il faut pouvoir avoir un aperçu de quelles opérations prennent le plus de temps. + +J'ai tout d'abords activé le mode de débogage sur la télévision, +qui peut être fait via l'API propriétaire de contrôle de la télévision. +Avec ce mode de débogage, on peut se connecter depuis une interface web aux outils de développeur du navigateur tournant sur la télévision. + +J'ai ensuite pu prendre plusieurs profils de performance pour observer quelles opérations prenait le plus de temps: + +\begin{figure}[H] + \includegraphics[width=\textwidth]{mood-tv-before.png} + \caption{Profil du chargement de la page, avant toute optimisation; annoté} + \label{moodtvbefore} +\end{figure} + +On peut séparer le temps de chargement en différentes étapes: + +\begin{enumerate} + \item Le chargement initial (en bleu), qui est le moment durant lequel le navigateur télécharge le code javascript, + et durant lequel \entity{webpack} prépare ce code pour reproduire un environnement modulaire moderne. + \item L'hydration \cite{hydration}, qui est une étape du rendu avec React: cette étape du chargement permet à React d'attacher + son arborescence d'état au HTML existant, permettant d'intéragir dans le code React avec les éléments du DOM. + \item La réception des \og catégories \fg, qui consiste au traitement de deux requêtes faites à l'API sur laquelle se trouve le \term{Content Management System} + \item L'envoi de requêtes pour récupérer les informations des films présents dans ces catégories + \\ + On peut déjà observer sur cette capture d'écran que cet envoi est en triple, alors qu'il ne devrait n'y avoir qu'une vague. + \item La réception des informations de films, et le traitement du résultat de ces requêtes. +\end{enumerate} + +Ensuite, il me fallait un moyen de mesurer de manière précise les performances avant de pouvoir faire des changements. +La solution que j'ai trouvé a été de créer et d'implémenter un protocole d'envoi et de réception de performances: \figref{moodtvperf} + +\begin{itemize} + \item La télévision mesure les performances avec l'API javascript \texttt{Performance} \cite{performanceapi}, + puis envoie ces valeurs via une requête sur un petit serveur sur mon ordinateur. + + \item Ce serveur stoque ensuite toutes les valeurs reçues dans un fichier, + et peut relire de ce fichier si jamais il faut reprendre la mesure de performance. + + \item Un programme lit les valeurs du fichier, puis calcule la moyenne, l'écart standard de ces valeurs. + Ce programme peut aussi lire deux fichiers, et comparer les performances entre ceux-cis. +\end{itemize} + +\begin{figure}[H] + \includegraphics[width=\textwidth]{moodtvperf} + \caption{Protocole d'envoi et de réception de performances} + \label{moodtvperf} +\end{figure} + +Les valeurs obtenues sont sous la forme de listes de nombres, une liste par métrique mesurée. +Soit $(\omega_1, \omega_2, ..., \omega_n)$ une telle liste. +Si on suppose que ces valeurs correspondent à différents résultats d'une même expérience $X$, +alors on peut calculer la moyenne empirique ($\mu^{\star}_n$) et la variance ($(\sigma^{\star}_n)^2$) empirique de ces valeurs de la manière suivante: + +\begin{align*} + \mu^{\star}_n &= \frac{\sum_{i=1}^{n} \omega_i}{n} \\ + (\sigma^{\star}_n)^2 = S_{n-1} &= \frac{\sum_{i=1}^{n} (\omega_i - \mu^{\star}_n)^2}{n-1} \\ + &= \frac{(\mu^{\star}_n)^2}{n-1} - \frac{\sum_{i=1}^{n} \omega_i^2}{n(n-1)} \\ \\ + \mu^{\star}_n \xrightarrow{n\rightarrow\infty} \mu & \quad \quad \quad \sigma^{\star}_n \xrightarrow{n\rightarrow\infty} \sigma + % TODO: preuve? +\end{align*} + +Afin de pouvoir comparer différents ensembles de valeurs, +je fais l'hypothèse que les valeurs suivent une loi normale ($X \hookrightarrow \mathcal{N}(\mu, \sigma^2)$). +% TODO: refine +Dans ce cas, on peut utiliser une approximation de la distribution de student pour calculer un intervale de confiance pour $\mu$: + +\begin{align*} + \mu &\in [\mu^{\star}_n - z_{1-\alpha/2} \frac{\sigma^{\star}_n}{\sqrt{n}}, \mu^{\star}_n + z_{1-\alpha/2} \frac{\sigma^{\star}_n}{\sqrt{n}}] \\ + z_a \;&\text{est tel que} \int_{-\infty}^{z_a}{\frac{e^{-\frac{(x-\mu)^2}{2\sigma^2}}}{\sigma\sqrt{2\pi}}}=a +\end{align*} + +Enfin, on peut utiliser le t-test pour estimer si deux valeurs différentes $X$ et $Y$ diffèrent bien de moyenne. \figref{ttest} + +\inputfig{perf1} + % Screenshot du panel des performances sur mood tv (avant et après) % Parler de la loi du milieu, du Z-test, du test de normalité, de comment récupérer ces données \subsection{Optimisation du temps de chargement} -% - [ ] Optimisation du nombre de requêtes faites (avec un dessin? ~ plus tard) +% - [x] Optimisation du nombre de requêtes faites (avec un dessin? ~ plus tard) % - [ ] Optimisation de la taille du bundle % - [ ] Optimisation du parsing des requêtes % Avoir de jolis nombres, et un tableau final avec toutes les mesures +La première optimisation a été d'éviter que les requêtes pour récupérer les informations sur les films ne soient envoyées plusieures fois. +La fonction pour faire la requête pour un seul film (\texttt{getItem}) utilisait un \texttt{useCallback} et un cache gêré par React, +ce qui faisait que dès que les informations d'un film était récupéré, React assignait une nouvelle référence à \texttt{getItem}, +causant le \texttt{useEffect} responsable pour charger l'ensemble des informations des films à tourner à nouveau, +et de nouvelles requêtes sont envoyées en conséquence. + +Pour résoudre ce problème, j'ai créé une nouvelle fonction qui récupère l'entièreté des informations des films, +et qui seulement une fois que toutes les requêtes soient finies stoque ces informations dans le cache. + +\inputfig{perf2} + +Ensuite, j'ai optimisé la taille du bundle javascript, pour passer de $350$ kilooctets à $243$ kilooctets, +en enlèvant les librairies qu'on n'utilisait peu et qui prenaient beaucoup de place, comme \texttt{browserify-crypto}. + +Réduire la taille du bundle permet de réduire le temps passé à télécharger le code javascript de la page, +et le temps passé à compiler ce code et à laisser webpack charger. + +\inputfig{perf3} + +Enfin, j'ai optimisé la logique de traitement des réponses + \subsection{Refactors} % - keyboard handler @@ -719,6 +829,7 @@ je pense qu'il est encore possible d'améliorer de différentes manières ce sys \inputfig{nestjs2} \inputfig{email-src-hbs} \inputfig{email-src-mjml} +\inputfig{ttest} \bibliographystyle{plain} \bibliography{references}{}