.Net Core contre .Net Framework
Si vous êtes encore un peu perdus sur ce qu’est le .Net Core et le .Net Framework, je vous conseille d’aller lire cet article de Gaël.
Microsoft a publié un article listant un certain nombre de comparaisons de performance entre le framework classique et le .Net Core. Les deux technologies étant celles de Microsoft et le Core étant open source, j’ai considéré qu’il n’était pas nécessaire d’en refaire d’autres, leurs résultats étant certainement fiables. Par contre, je vous en propose une synthèse :
Grâce à cette synthèse, on voit que Microsoft a beaucoup travaillé pour améliorer les performances sur le .Net Core par rapport au .Net Framework. Certains sujets restent encore à la traîne, mais d’autres ont été revus en profondeur. LINQ devenu omniprésent ces dernières années a pris une bonne dose de stéroïdes. La sérialisation, la compression, le networking, la concurrency eux aussi très actuels à l’heure des Web API sont bien plus rapides en .Net Core.
Face à Node.js
Il y a de nombreuses raisons de comparer une application Web .Net Core à Node.js. La simplicité de mise en oeuvre d’un service web dans les deux technologies. Quelques lignes de codes suffisent. Elles sont un peu plus nombreuses en C#, mais elles sont générées par une simple ligne de commande. De plus, il ne fait aucun doute que Microsoft vise à concurrencer Node.js avec son .Net Core. Mais la principale raison de les comparer est que les applications web sont hébergées par Kestrel en .Net Core. Or Kestrel est basé sur libuv, comme Node.js. Je vous propose donc de comparer .Net Core et Node.js sur des exemples simples : deux helloworld.
Le code
Les deux exemples auront le code minimal pour répondre un 200(OK) à un GET. En un mot un HelloWorld ! tout simple. Vous trouverez le code plus bas.
Node.js
La version utilisée est la v8.9.4 sur tous les OS. Le code est le plus simple possible. Le voici:
var http = require("http"); http.createServer(function (request, response) { response.writeHead(200, {'Content-Type': 'text/plain'}); response.end('Hello Worldn'); }).listen(8081);
ASP.Net Core
La version utilisée est la 2.0.5 avec SDK 2.1.4. sur tous les OS.
Le serveur est le projet web de base créé avec la ligne de commande dotnet new web
.
La web application Node.js ne donne aucun log, or par défaut la web application .Net Core donne plusieurs lignes par requête. Les sorties ralentissant beaucoup un process, on a coupé les logs en .Net Core en insérant la ligne suivante dans le corps du WebHostBuilder de program.cs:
.ConfigureLogging( (lb) => { lb.AddFilter((ll) => false); })
Cette ligne ajoute un filtre au log builder, le filtre se basant sur le log level et renvoyant false
quel qu’il soit.
Il est compilé en mode release spécifiquement pour l’OS avec la commande suivante.
Ex :
$ dotnet build --configuration Release --runtime ubuntu.16.04-x64
Vous pouvez trouver la liste des runtimes ici.
Le contexte
Le client de test est bombardier. C’est une application écrite en GO. J’ai utilisé les binaires déjà compilés que l’on peut trouver ici : https://github.com/codesenberg/bombardier/releases en V1.1.1
- Linux: bombardier-linux-amd64
- Windows: bombardier-windows-amd64.exe
Le nombre de requêtes
Ce paramètre influe peu sur les mesures sauf dans des cas particuliers. Donc j’ai pris des valeurs différentes entre Linux et Windows, Windows étant beaucoup plus lent. L’autre raison de ce choix vient de la durée des tests (entre 5 et 8 minutes), ce qui éloigne les perturbations dues à des phénomènes transitoires.
Nombre de connexions
Pour les tests j’ai choisi un nombre de 512 connexions. Ce paramètre a une grande influence sur les chiffres mesurés. Par exemple en doublant ce chiffre le nombre de requêtes traitées à la seconde augmente d’un facteur 4. Mais cette influence est à peu près la même sur tous les types de projet.
Remarques diverses
- Lors des tests le monitoring system était ouvert et a vérifié que le CPU était bien consommé par l’appli .Net Core ou Node.js et pas bombardier.
- L’impact sur la mémoire étant peu significatif, il n’a pas été mesuré précisément.
- Pour chaque test vous trouverez une capture de la sortie de bombardier. C’est une parmi plusieurs autres, car on a réalisé les expériences plusieurs fois. J’en ai choisi une dans la moyenne. Sachant qu’il peut y avoir des écarts de 30% entre la meilleure et la moins bonne.
Ubuntu 16.0.4
- Machine : PC portable Aspire V3-772G
- OS : Ubuntu 16.04 Kernel~4.13.0-26-generic x86_64
- CPU : Dual core Intel Core i5-6200U (-HT-MCP-) speed/max~2400/2800 MHz
- Mémoire : 16Go
ASP.Net Core
$ ./bombardier-linux-amd64 -c 512 -n 10000000 https://localhost:5000 Bombarding https://localhost:5000 with 10000000 requests using 512 connections 10000000 / 10000000 [============================================] 100.00% 2m8s Done! Statistics Avg Stdev Max Reqs/sec 78111.42 12938.56 389665.82 Latency 6.59ms 6.77ms 1.28s HTTP codes: 1xx - 0, 2xx - 10000000, 3xx - 0, 4xx - 0, 5xx - 0 others - 0 Throughput: 13.70MB/s
Node.js
$ ./bombardier-linux-amd64 -c 512 -n 10000000 https://localhost:8081 Bombarding https://localhost:8081 with 10000000 requests using 512 connections 10000000 / 10000000 [============================================] 100.00% 7m0s Done! Statistics Avg Stdev Max Reqs/sec 23784.13 2946.99 32254.90 Latency 21.52ms 3.94ms 1.13s HTTP codes: 1xx - 0, 2xx - 10000000, 3xx - 0, 4xx - 0, 5xx - 0 others - 0 Throughput: 4.94MB/s
.Net Core est largement en tête, il y a un facteur 2,7 sur le débit de données et même de 3,2 sur le nombre de requêtes.
Amazon Linux AWS Lightsail
J‘ai pris l’offre de base à 5$ (gratuit 1 mois) à savoir une machine 1 coeur, 512 de RAM et un SSD de 20Go.
Pour la version exacte de l’OS :
$ cat /etc/*-release NAME="Amazon Linux AMI" VERSION="2017.09" ID="amzn" ID_LIKE="rhel fedora" VERSION_ID="2017.09" PRETTY_NAME="Amazon Linux AMI 2017.09" ANSI_COLOR="0;33" CPE_NAME="cpe:/o:amazon:linux:2017.09:ga" HOME_URL="https://aws.amazon.com/amazon-linux-ami/" Amazon Linux AMI release 2017.09
ASP.Net Core
$ ./bombardier-linux-amd64 -c 512 -n 1000000 https://localhost:5000 Bombarding https://localhost:5000 with 1000000 requests using 512 connections 1000000 / 1000000 [===================================================================================================================================] 100.00% 1m0s Done! Statistics Avg Stdev Max Reqs/sec 16696.79 3887.70 38553.78 Latency 30.69ms 3.32ms 181.37ms HTTP codes: 1xx - 0, 2xx - 1000000, 3xx - 0, 4xx - 0, 5xx - 0 others - 0 Throughput: 2.94MB/s
Node.js
$ ./bombardier-linux-amd64 -c 512 -n 1000000 https://localhost:8081 Bombarding https://localhost:5000 with 1000000 requests using 512 connections 1000000 / 1000000 [===================================================================================================================================] 100.00% 2m4s Done! Statistics Avg Stdev Max Reqs/sec 8042.10 591.97 11051.52 Latency 63.64ms 14.98ms 1.40s HTTP codes: 1xx - 0, 2xx - 1000000, 3xx - 0, 4xx - 0, 5xx - 0 others - 0 Throughput: 1.49MB/s
.Net Core reste largement en tête d’environ un facteur 2.
Comparé à Ubuntu, .Net Core divise son débit de 4,6.
Node.js lui ne voit son débit diminuer que de 3,3.
Le fait que cette machine virtuelle soit mono core explique certainement les résultats, en effet on peut penser que .Net Core exploite mieux les 4 cores de l’aspire V3. Donc passer à une machine mono core le pénalise plus que Node.js.
Windows 10
La machine est la même que pour Ubuntu (dual boot) à savoir un PC portable V3-772G.
ASP.Net Core
> bombardier-windows-amd64.exe -c 512 -n 10000000 https://localhost:5000 Bombarding https://localhost:5000 with 10000000 requests using 512 connections 10000000 / 10000000 [==================================================================================] 100.00% 2m32s Done! Statistics Avg Stdev Max Reqs/sec 66191.11 9760.25 127907.27 Latency 7.82ms 1.36ms 587.42ms HTTP codes: 1xx - 0, 2xx - 10000000, 3xx - 0, 4xx - 0, 5xx - 0 others - 0 Throughput: 11.55MB/s
Node.js
> bombardier-windows-amd64.exe -c 512 -n 10000000 https://localhost:8081 Bombarding https://localhost:8081 with 10000000 requests using 512 connections 10000000 / 10000000 [==================================================================================] 100.00% 7m46s Done! Statistics Avg Stdev Max Reqs/sec 21435.67 1338.44 22884.21 Latency 23.88ms 1.44ms 754.54ms HTTP codes: 1xx - 0, 2xx - 10000000, 3xx - 0, 4xx - 0, 5xx - 0 others - 0 Throughput: 4.46MB/s
Encore une fois .Net Core reste meilleur que Node.js (x 2,6). Bien que le hardware soit identique que sur Ubuntu, sans surprise les résultats sont moins bons : .Net Core -20% Node.js -10%; mais finalement ce n’est pas tant le cas que cela. Si on regarde le StdDev assez important on peut estimer que les différences sont dans l’erreur de mesure.
Windows Server 2012 R2 sur le cloud Azure
Setup : virtual machine Quad core à 2.3GHz
ASP.Net Core
> bombardier-windows-amd64.exe -c 512 -n 10000000 https://localhost:5000 Bombarding https://localhost:5000 with 10000000 requests using 512 connections 10000000 / 10000000 [==========================================] 100.00% 2m44s Done! Statistics Avg Stdev Max Reqs/sec 60567.10 13705.29 164975.25 Reqs/sec 8.40ms 334.47us 104.00ms HTTP codes: 1xx - 0, 2xx - 10000000, 3xx - 0, 4xx - 0, 5xx - 0 others - 0 Throughput: 10.72MB/s
Node.js
> bombardier-windows-amd64.exe -c 512 -n 10000000 https://localhost:8081 Bombarding https://localhost:8081 with 10000000 requests using 512 connections 10000000 / 10000000 [=========================================] 100.00% 11m46s Done! Statistics Avg Stdev Max Reqs/sec 14166.61 1292.90 36872.50 Reqs/sec 36.16ms 2.19ms 338.00ms HTTP codes: 1xx - 0, 2xx - 10000000, 3xx - 0, 4xx - 0, 5xx - 0 others - 0 Throughput: 2.62MB/s
Encore une fois .Net Core est devant. Les résultats restent cohérents avec une version grand public de Windows. Le fait que la machine soit virtuelle reste un peu pénalisant, mais tant que l’on conserve un nombre de coeurs identique, la baisse reste raisonnable.
Conclusion
La première remarque est que le travail de Microsoft résumé dans la 1ère partie de l’article a payé. Et ce travail ne semble pas fini si on lit la road map du .Net Core 2.1.
Node.js est clairement dépassé quelque soit l’OS, le type de machine et le nombre de coeurs. Il aurait été dommage que les performances .Net Core soient en retrait, car c’est affiché clairement comme argument par Microsoft.
La petite demi surprise c’est les bons chiffres sur Ubuntu, on sent que la firme de Redmond a vraiment travaillé l’implémentation du .Net Core sur Linux.
Après même si cela n’apparaît pas dans les chiffres, il y a tout de même quelques bémols. En effet sans la désactivation des logs, les performances de l’application Web .Net Core sont divisées par environ 5. Ce qui, lors de nos tests sur windows, causait même des erreurs sur 1% à 2% des requêtes. Or une application sans log, n’est pas très réaliste en production. De même que sans authentification ou même sans requêter un autre service dans un univers SOA. Je vous conseille donc de faire vos propres tests. Je vous conseille aussi de considérer les fonctions facilitant vos développements comme le nouveau concept de Middleware et de Pipeline.
Vos commentaires
Intéressant, je suis a la recherche de benchmarks sur des applications un peu plus élaborées. renvoyer un hello world au client n’ étant pas un véritable use case. curieux aussi de la vitesse d’itération sur du .NET core , ce qui me plait bien avec node c’est la rapidité avec laquelle tu peux développer.
L’équipe .Net Core utilise le benchmark de techempower pour comparer les performances entre framework webs : https://www.techempower.com/benchmarks/#section=data-r17&hw=ph&test=fortune
Voici le github :
https://github.com/aspnet/Benchmarks
Sur le github ils ont mis un petit comparatif avec justement Node.js VS AspNetCore :
https://aka.ms/aspnet/benchmarks