From 40f79c1f19e4993f9631c166274613eb52387ec6 Mon Sep 17 00:00:00 2001 From: kataras Date: Mon, 21 Aug 2017 16:17:32 +0300 Subject: [PATCH] Kestrel vs Iris new benchmarks, as you asked for :+1: Former-commit-id: d8979e55679bbb586dd79a81a43e1c29082ad9c2 --- _benchmarks/README.md | 115 ++++++++++++++------ _benchmarks/iris/main.go | 20 +--- _benchmarks/netcore/Program.cs | 25 +++++ _benchmarks/netcore/Startup.cs | 38 +++++++ _benchmarks/netcore/appsettings.json | 8 ++ _benchmarks/netcore/netcore.csproj | 15 +++ _benchmarks/screens/1m_requests_iris.png | Bin 0 -> 11606 bytes _benchmarks/screens/1m_requests_netcore.png | Bin 0 -> 11508 bytes 8 files changed, 171 insertions(+), 50 deletions(-) create mode 100644 _benchmarks/netcore/Program.cs create mode 100644 _benchmarks/netcore/Startup.cs create mode 100644 _benchmarks/netcore/appsettings.json create mode 100644 _benchmarks/netcore/netcore.csproj create mode 100644 _benchmarks/screens/1m_requests_iris.png create mode 100644 _benchmarks/screens/1m_requests_netcore.png diff --git a/_benchmarks/README.md b/_benchmarks/README.md index b8217e4e..92ff71ff 100644 --- a/_benchmarks/README.md +++ b/_benchmarks/README.md @@ -10,7 +10,11 @@ * **.NET Core**: https://www.microsoft.com/net/core, latest version **2.0** * **Iris**: https://github.com/kataras/iris, latest version **8.3** built with [go1.8.3](https://golang.org) -## Results +# .NET Core MVC vs Iris MVC + +The first test will contain a simple application with a text response and the second will render templates + a layout. + +## Simple We will compare two identical things here, in terms of application, the expected response and the stability of their run times, so we will not try to put more things in the game like `JSON` or `XML` encoders and decoders, just a simple text message. To achieve a fair comparison we will use the MVC architecture pattern on both sides, Go and .NET Core. @@ -60,31 +64,10 @@ Statistics Avg Stdev Max Throughput: 19.65MB/s ``` -### Iris -```bash -$ cd iris -$ go run main.go -Now listening on: http://localhost:5000 -Application started. Press CTRL+C to shut down. -``` - -```bash -$ bombardier -c 125 -n 5000000 http://localhost:5000/api/values/5 -Bombarding http://localhost:5000/api/values/5 with 5000000 requests using 125 connections - 5000000 / 5000000 [======================================================================================] 100.00% 45s -Done! -Statistics Avg Stdev Max - Reqs/sec 110809.98 8209.87 128212 - Latency 1.13ms 307.86us 18.02ms - HTTP codes: - 1xx - 0, 2xx - 5000000, 3xx - 0, 4xx - 0, 5xx - 0 - others - 0 - Throughput: 20.61MB/s -``` Click [here](screens) to navigate to the screenshots. -#### Summary +### Summary * Time to complete the `5000000 requests` - smaller is better. * Reqs/sec - bigger is better. @@ -97,8 +80,6 @@ Click [here](screens) to navigate to the screenshots. Iris MVC Application, written using 27 lines of code, ran for **47 seconds** serving **105643.71** requests per second within **1.18ms** latency in average and **22.01ms** max, the memory usage of all these was ~12MB. -Iris Application, written using 22 lines of code, ran for **45 seconds** serving **110809.98** requests per second within **1.13ms** latency in average and **18.02ms** max, the memory usage of all these was ~11MB. - #### Update: 20 August 2017 As [Josh Clark](https://twitter.com/clarkis117) and [Scott Hanselman‏](https://twitter.com/shanselman)‏ pointed out [on this status](https://twitter.com/shanselman/status/899005786826788865), on .NET Core MVC `Startup.cs` file the line with `services.AddMvc();` can be replaced with `services.AddMvcCore();`. I followed their helpful instructions and re-run the benchmarks. The article now contains the latest benchmark output for the .NET Core application with the change both Josh and Scott noted. @@ -107,7 +88,7 @@ The twitter conversion: https://twitter.com/MakisMaropoulos/status/8991132158959 For those who want to compare with the standard services.AddMvc(); you can see the old output by pressing [here](screens/5m_requests_netcore-mvc.png). -## Results with Templates +## MVC + Templates Let’s run one more benchmark, spawn `1000000 requests` but this time we expect HTML generated by templates via the view engine. @@ -157,7 +138,7 @@ Statistics Avg Stdev Max Throughput: 192.51MB/s ``` -#### Summary +### Summary * Time to complete the `1000000 requests` - smaller is better. * Reqs/sec - bigger is better. @@ -169,14 +150,79 @@ Statistics Avg Stdev Max Iris MVC with Templates Application ran for **37 seconds** serving **26656.76** requests per second with **192.51MB/s** within **1.18ms** latency in average and **22.52ms** max, the memory usage of all these was ~17MB. -## Results with Sessions +# .NET Core (Kestrel) vs Iris -Here we will check the sessions performance, this time -we wanna use the .NET Core raw, **not MVC** and Iris raw, not MVC respectfully. +_Monday, 21 August 2017_ -Spawn `5000000 requests` with 125 different "threads" that sets and gets a session with name `key` and string value `"value"` to the same static request path. +This time we will compare the speed of the “low-level” .NET Core’s server implementation named Kestrel and Iris’ “low-level” handlers, we will test two simple applications, the first will be the same as our previous application but written using handlers and the second test will contain a single route which sets and gets a session value(string) based on a key(string). -### .NET Core with Sessions +## Simple + +Spawn `1000000 requests` with 125 different "threads", tagrget to a dynamic registered route path, responds with a simple "value" text. + +### .NET Core (Kestrel) + +```bash +$ cd netcore +$ dotnet run -c Release +Hosting environment: Production +Content root path: C:\mygopath\src\github.com\kataras\iris\_benchmarks\netcore +Now listening on: http://localhost:5000 +Application started. Press Ctrl+C to shut down. +``` + +```bash +Bombarding http://localhost:5000/api/values/5 with 1000000 requests using 125 connections + 1000000 / 1000000 [======================================================================================] 100.00% 10s +Done! +Statistics Avg Stdev Max + Reqs/sec 97884.57 8699.94 110509 + Latency 1.28ms 682.63us 61.04ms + HTTP codes: + 1xx - 0, 2xx - 1000000, 3xx - 0, 4xx - 0, 5xx - 0 + others - 0 + Throughput: 17.73MB/s +``` + +### Iris + +```bash +$ cd iris +$ go run main.go +Now listening on: http://localhost:5000 +Application started. Press CTRL+C to shut down. +``` + +```bash +Bombarding http://localhost:5000/api/values/5 with 1000000 requests using 125 connections + 1000000 / 1000000 [=======================================================================================] 100.00% 8s +Done! +Statistics Avg Stdev Max + Reqs/sec 117917.79 4437.04 125614 + Latency 1.06ms 278.12us 19.03ms + HTTP codes: + 1xx - 0, 2xx - 1000000, 3xx - 0, 4xx - 0, 5xx - 0 + others - 0 + Throughput: 21.93MB/s +``` + +### Summary + +* Time to complete the `1000000 requests` - smaller is better. +* Reqs/sec - bigger is better. +* Latency - smaller is better +* Throughput - bigger is better. +* LOC (Lines Of Code) - smaller is better. + +.NET Core (Kestrel) Application written using **63 code of lines** ran for **10 seconds** serving **97884.57** requests per second with **17.73MB/s** within **1.28ms** latency in average and **61.04ms** max. + +Iris Application written using **14 code of lines** ran for **8 seconds** serving **117917.79** requests per second with **21.93MB/s** within **1.06ms** latency in average and **19.03ms** max. + +## Sessions + +Spawn `5000000 requests` with 125 different "threads" target a static request path, sets and gets a session with name `key` and string value `"value"` and write session value that to the response stream. + +### .NET Core (Kestrel) with Sessions ```bash $ cd netcore-sessions @@ -222,7 +268,7 @@ Statistics Avg Stdev Max Throughput: 20.65MB/s ``` -#### Summary +### Summary * Time to complete the `5000000 requests` - smaller is better. * Reqs/sec - bigger is better. @@ -233,6 +279,9 @@ Statistics Avg Stdev Max Iris with Sessions Application ran for **1 minute and 15 seconds** serving **66749.70** requests per second with **20.65MB/s** within **1.88ms** latency in average and **1.94s** max. +> A new article based on the latest, Kestrel and Iris, benchmarks is coming. Stay tuned! + +> Click [here](screens) to navigate to the screenshots. **Thank you all** for the 100% green feedback, have fun! diff --git a/_benchmarks/iris/main.go b/_benchmarks/iris/main.go index e3f9847d..a8e296a9 100644 --- a/_benchmarks/iris/main.go +++ b/_benchmarks/iris/main.go @@ -7,22 +7,8 @@ import ( func main() { app := iris.New() - // These handlers are serving the same routes as - // `ValuesController`s of netcore-mvc and iris-mvc applications do. - app.Get("/api/values/{id}", getHandler) - app.Put("/api/values/{id}", putHandler) - app.Delete("/api/values/{id}", delHandler) + app.Get("/api/values/{id}", func(ctx context.Context) { + ctx.WriteString("value") + }) app.Run(iris.Addr(":5000")) } - -// getHandler handles "GET" requests to "api/values/{id}". -func getHandler(ctx context.Context) { - // id,_ := vc.Params.GetInt("id") - ctx.WriteString("value") -} - -// putHandler handles "PUT" requests to "api/values/{id}". -func putHandler(ctx context.Context) {} - -// delHandler handles "DELETE" requests to "api/values/{id}". -func delHandler(ctx context.Context) {} diff --git a/_benchmarks/netcore/Program.cs b/_benchmarks/netcore/Program.cs new file mode 100644 index 00000000..92bb511a --- /dev/null +++ b/_benchmarks/netcore/Program.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; + +namespace netcore +{ + public class Program + { + public static void Main(string[] args) + { + BuildWebHost(args).Run(); + } + + public static IWebHost BuildWebHost(string[] args) => + WebHost.CreateDefaultBuilder(args) + .UseStartup() + .Build(); + } +} diff --git a/_benchmarks/netcore/Startup.cs b/_benchmarks/netcore/Startup.cs new file mode 100644 index 00000000..f5ff0edd --- /dev/null +++ b/_benchmarks/netcore/Startup.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.AspNetCore.Routing; +using Microsoft.AspNetCore.Http; + +namespace netcore +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + public void ConfigureServices(IServiceCollection services) + { + services.AddRouting(); + } + + public void Configure(IApplicationBuilder app, IHostingEnvironment env) + { + var routeBuilder = new RouteBuilder(app); + routeBuilder.MapGet("api/values/{id}", context =>{ + return context.Response.WriteAsync("value"); + }); + var routes = routeBuilder.Build(); + app.UseRouter(routes); + } + } +} diff --git a/_benchmarks/netcore/appsettings.json b/_benchmarks/netcore/appsettings.json new file mode 100644 index 00000000..50fe9a31 --- /dev/null +++ b/_benchmarks/netcore/appsettings.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "IncludeScopes": false, + "LogLevel": { + "Default": "Error" + } + } +} diff --git a/_benchmarks/netcore/netcore.csproj b/_benchmarks/netcore/netcore.csproj new file mode 100644 index 00000000..b36fc56a --- /dev/null +++ b/_benchmarks/netcore/netcore.csproj @@ -0,0 +1,15 @@ + + + + netcoreapp2.0 + + + + + + + + + + + diff --git a/_benchmarks/screens/1m_requests_iris.png b/_benchmarks/screens/1m_requests_iris.png new file mode 100644 index 0000000000000000000000000000000000000000..78617a9a8bfeff1a1cf010ddada539bdc8b4f83e GIT binary patch literal 11606 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 literal 0 HcmV?d00001 diff --git a/_benchmarks/screens/1m_requests_netcore.png b/_benchmarks/screens/1m_requests_netcore.png new file mode 100644 index 0000000000000000000000000000000000000000..7e36b7ef9408fa17d2bb59cf33e43a7dbb630060 GIT binary patch literal 11508 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 P1=GHvtC4%n%Kv`=J;jU7 literal 0 HcmV?d00001