GRPC vs WCF - Une question de performances

Une comparaison entre deux frameworks de serveur web

Dans ce billet, nous allons vous communiquer les résultats que nous avons obtenus lors d’une étude sur les performances entre deux frameworks web pour créer des webservices. Nous hésitions entre GRPC et WCF et nous devions prendre une décision sur base de données factuelles, il n’y avait pas vraiment de religion au sein de l’équipe. Nous avons donc mené des petites expériences en vue d’aiguiller notre choix et nous espérons que les conclusions pourront vous aider à vous faire vos propres décisions si vous êtes confrontés à un problème analogue.

Contexte

Nous avons mené une petite expérience en vue de comparer les performances entre deux frameworks pour créer des webservices. Cette étude a été motivée suite à la réécriture d’une application interne due à une migration dans le cloud. Les utilisateurs (environs 150 actifs en même temps) modifient en continu les données de graphe présentes dans une grosse base de données (1,5To) avec des requêtes "assez simples": récupération des liens, fusion des nœuds, ajout de nouveaux éléments. Mais qui, pour des raisons business, sont assez complexes en pratique (jointure sur 5 tables pour récupérer les liens, plusieurs recherches avant de mettre à jour les données, …). Il est donc assez important d’avoir les meilleurs performances possibles. L’architecture est fort classique, il s’agit d’un trois tiers.

Technologie

Deux technologies étaient à l’étude, bon gré, mal gré. L’API se devait d’être performante et ne nécessitait pas d’être accessible, uniquement interne. À titre personnel, j’aurais aimé voir GraphQL ajouté à la comparaison.

WCF

Windows Communication Foundations est un produit créé par Microsoft et intégré depuis .net framework 3.0 (2006 !) (et qui trouve ses origines dans .net remoting). C’est un système de messagerie orienté services en SOAP, et offrait une alternative à REST qui commençait à connaître son envol. Seulement, il a reçu un accueil mitigé suite au refus d’utilisation/implémentation par le monde Java, la politique plus que discutable de Microsoft durant ces années et l’arrivée tardive de fonctionnalités de type REST et de JSON. Il reste néanmoins une alternative valable dans le monde du RPC en C# surtout qu’il est nativement disponible sur tous les ordinateurs à partir de Windows 8.

GRPC

General-purpose Remote Procedure Calls est le bébé créé par Google, il est beaucoup plus récent (2015) et visait une autre approche du problème, sans doute inspirée du projet OpenAPI qui émergeait de Swagger. Au lieu de fournir une interface qu’il faut implémenter tant au niveau du code utilisateur qu’au niveau de la communication, l’idée est de définir des fichiers communs (.proto) à partir desquels on peut générer les interfaces utilisateur. Il est alors plus aisé d’employer les mêmes objets dans un autre langage sans devoir apprendre les spécificités propres à l’interface liée au langage (l’enfer des annotations).

Expériences

Nous avons mené 4 expériences:

  • Le cas de base. Le temps mis pour pour récupérer des liens attachés à un nœud choisi de manière aléatoire. Il faut savoir que l’immense majorité (98%) de nos nœuds, ont moins de deux liens. Il y a un très gros effet de "Rich-club phenomenon", les nœuds ayant le plus de liens sont souvent interconnectés. Donc, en pratique, on va faire une "requête de base" au serveur qui va retourner très peu de résultats.
  • Le pire cas associé. Cette fois-ci, on s’intéresse aux nœuds ayant le plus de relations.
  • Une recherche. Les utilisateurs peuvent rechercher des nœuds sur base du nom et on retourne le top 10.000.
  • Le temps de transfert. On préenregistre une requête du côté serveur et on regarde le temps mis pour transférer X éléments de cette collection.

Tous les résultats furent mesurés du côté client, dans les locaux ("on premise") par rapport à un webservice et une base de données dans le cloud. Il s’agit également de .net framework 4.6. Nous aurions aimé voir l’impact de .net core 3 mais ce n’était pas possible. Nous avons employé 10 threads et non davantage parce que nous avons observé que cela correspondait à une sorte de maximums de nombre de requêtes simultanées effectuées par les utilisateurs (ils sont peu nombreux - 150 et n’en font pas toutes les millisecondes). WCF était configuré en TCP.

Cas de base

L’expérience est relativement simple et vise surtout à mesurer le temps mis par l’infrastructure en général. Un RTT complet. On effectue 10.000 requêtes avec 10 threads où chacun demande les liens attachés à son nœud courant. La complexité vient du fait que la requête se fait sur une table contenant 2 milliards de liens et 600 millions d’entités avec 5 jointures, et en effet, la base de données prend l’essentiel du temps.

Résultat surprenant et que nous avouons avoir du mal à expliquer. Là où WCF présente un écart type quasiment nul, GRPC en présente. La distinction est peut-être faussée par un panier de l’histogramme pile au mauvais endroit pour GRPC qui coupe arbitrairement la distribution en deux. Mais l’essentiel de la population reste dans les 20% de WCF sur des temps "négligeables". Par contre, on a quelques éléments qui sortent du lot. On a pu déterminer que la requête en base de données prenait aux alentours de 80ms et le temps lié à la communication au travers de l’internet est aux alentours de 20ms.

Pire cas

On effectue la même requête que précédemment mais cette fois-ci sur des entités connues comme ayant "beaucoup" de liens (on ne dépasse pas les 250.000 liens pour deux-trois cas pathologiques).

Là, la situation est nettement plus claire. La distribution s’est nettement déplacée vers la gauche. On voit moins de cas exceptionnels pour GRPC alors qu’ils sont légions en WCF. On commence à percevoir que GRPC est nettement plus efficace pour sérialiser des gros paquets.

Recherche

Ici, on recherche les 10.000 premiers nœuds retournés par la recherche sur base du nom. C’est une opération couramment effectuée par les usagers avant d’appliquer davantage de filtres sur leurs recherches. Cette recherche peut tant retourner aucun résultat que des millions si les mots sont mal choisis, mais nous avons limité à 100.000 dans l’absolu.

On retrouve la même situation que précédemment, le gain apporté par GRPC est assez substantiel et réduit énormément le nombre de valeurs aberrantes dont les utilisateurs se plaignent.

Sérialisation

Dans cette expérience, nous avons voulu voir quel était le coût réel de la sérialisation avec WCF. Nous avons essayé plusieurs situations en compressant ou non les données mais les résultats étaient peu consistants, la compression apportait énormément pour les grosses requêtes mais rajoutait beaucoup de temps sur les petites. De même l’utilisation CPU grimpait dans des proportions peu satisfaisantes … On a donc effectué une prérecherche en stockant les 4.000 premiers résultats. Ensuite, on demande séquentiellement les X premiers résultats toujours avec 10 threads.

Résultat plus que surprenant, nous imaginions bien que le fait d’employer du XML de manière sous-jacente avec WCF n’était pas déjà un bon point mais alors pour sérialiser de gros objets (60 attributs avec plusieurs strings), GRPC n’a d’autre égale. L’écart-type devient effrayant avec WCF alors qu’il reste contenu avec GRPC.

Autres métriques

Lors de ces petites expériences, nous avons également mesuré d’autres métriques comme la consommation mémoire ou la bande passante grâce à l’Event Tracing for Windows (ETW).

Bande passante

Difficile d’avoir une représentation graphique pour ceci. Tant sur la taille des messages envoyés que celle des messages reçus, nous avons observé un facteur entre 3 et 3.5 !

Consommation mémoire

Même chose, situation difficile à retranscrire de manière visuelle. Voici une représentation de la consommation mémoire toutes les 500 millisecondes.

On observe deux choses sur ce graphique. Premièrement, il y a moins de mesure en GRPC parce que le programme s’est exécuté plus rapidement. Deuxièmement, la consommation mémoire est difficile à évaluer et on peut difficilement en conclure quoi que ce soit. Notamment parce qu’il s’agit d’un projet écrit en C#, employant donc un Garbage Collector.

Autre vue

Afin de communiquer à l’équipe business nos résultats, nous avons cherché à présenter de manière plus intuitive et surtout de manière chiffrée. Nous avons pensé à un graphique de ce type:

En expliquant que, en ordonnées, on représente la différence de temps entre GRPC et WCF. Plus la différence est négative, plus c’est à l’avantage de GRPC et si c’est positif, c’est en faveur de WCF. En abscisse, on retrouve une représentation des quantiles associés à la fonction de répartition empirique (ecdf pour les moldus), quel pourcentage de la population bénéficie de quel gain en performance. Par exemple, au quantile 20, on a plus ou moins -900ms. Ce qui peut être interprété comme "20% de la population verra son temps d’exécution réduit de pratiquement 1 seconde ou plus". Et, en général, on gagne un tiers de seconde par requête (ce qui n’est pas très impressionnant comme résultat au final).

Conclusions

À titre personnel, nous avons opté pour GRPC suite aux différents résultats que nous avons observés. Ce que nous cherchions surtout à minimiser était les cas pathologiques. On voulait éviter d’avoir des requêtes sensiblement plus lentes pour l’utilisateur, qu’ils aient une meilleur perception de vitesse. De fait, les utilisateurs travaillent essentiellement sur les nœuds ayant un certain nombre de relations puisqu’ils sont davantage sujets à modifications, et présentent souvent un intérêt business accru. Mais nous voyons que WCF peut se défendre sur des messages simples. De manière générale, nous avons observé que GRPC était plus efficace. De plus, Microsoft semble avoir abandonné le projet WCF suite à son manque de popularité et son interopérabilité quelque peu manquante …

En résumé, employez GRPC en 2020 !



Aucun commentaire

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte