Supposons que vous travailliez sur un problème de performance d’un scénario sur un serveur et que, suite à une première campagne de suppression des goulets d’étranglements, vous ayez réussi à abaisser le temps total d’exécution du scénario.
La plupart des temps unitaires d’exécution des requêtes ont baissé, le nombre de scénarios joués en parallèle dans le même temps augmente, etc. Tout se passe donc a priori dans le bon sens, mais vous vous rendez compte que certaines requêtes prennent plus de temps. Et pas seulement relativement au scénario dans son ensemble, mais en temps absolu. Les performances sont globalement meilleures, mais certains temps d’exécution s’allongent !
Surtout, vous vous rendez compte que ce n’est pas un effet de bord sur un temps unitaire, mais bien sur une moyenne pour une requête particulière. Comment est-ce possible ?
Une des raisons possibles est que l’augmentation des performances globales fait que plus de threads peuvent être menées en parallèle, et plus de jeux de scénarios peuvent être exécutés de manière rapprochée. Au final, des verrous qui, avec un flux faible étaient invisibles, peuvent devenir prépondérant, et ainsi ralentir les opérations associées.
Un petit graphe temporel pour expliquer ceci : avant optimisation, les instances se succèdent à un rythme faible, et le lock est pour ainsi dire nul.
Prenons maintenant le même graphe où le throughput a fortement augmenté, car les goulets d’étranglements principaux ont été supprimés. On peut alors se retrouver avec ce genre de graphique :
La période de recouvrement est alors beaucoup plus longue et que se passe-t-il alors ? Si vous avez malheureusement une contention sur une ressource unique, le second service devra attendre la fin du premier, ou en tout cas le moment où il relâche cette ressource, pour continuer, ralentissant ainsi son exécution. Si la période de recouvrement augmente, on peut arriver à des extrêmes comme ceci :
Les zones en rouge montrent l’exécution effective de la requête, en supposant une ressource unique partagée, et relâchée seulement en toute fin d’exécution. Les lignes bleues montrent alors le temps total d’exécution perçue pour une requête donnée. On voit bien que plus de throughtput (et donc des lancements plus rapprochés) peuvent dans certains cas provoquer une durée au final plus longue, alors que globalement, la performance reste meilleure, au sens où il est possible de tirer plus de scénarios dans le même temps, à machine équivalente…
En conclusion : attention à la façon dont on mesure la performance. En cas de doute, toujours revenir aux bases : comment est-ce que la performance est perçue par le client ?