From 20e2dd694f0dea65d5d235f7c7a1fd9aff94c6d7 Mon Sep 17 00:00:00 2001 From: "Gerasimos (Makis) Maropoulos" Date: Fri, 29 Sep 2017 15:35:51 +0300 Subject: [PATCH] Add expressjs benchmarks similar to iris and netcore but MVC Former-commit-id: 0f56ebf040514ee332b6b4a13ecc02ce6cd339c7 --- README.md | 2 +- _benchmarks/README.md | 65 ++- _benchmarks/expressjs-sessions/app.js | 26 ++ .../expressjs-sessions/package-lock.json | 396 ++++++++++++++++++ _benchmarks/expressjs-sessions/package.json | 15 + _benchmarks/expressjs/app.js | 14 + _benchmarks/expressjs/package-lock.json | 357 ++++++++++++++++ _benchmarks/expressjs/package.json | 14 + _benchmarks/screens/1m_requests_expressjs.png | Bin 0 -> 11581 bytes 9 files changed, 883 insertions(+), 6 deletions(-) create mode 100644 _benchmarks/expressjs-sessions/app.js create mode 100644 _benchmarks/expressjs-sessions/package-lock.json create mode 100644 _benchmarks/expressjs-sessions/package.json create mode 100644 _benchmarks/expressjs/app.js create mode 100644 _benchmarks/expressjs/package-lock.json create mode 100644 _benchmarks/expressjs/package.json create mode 100644 _benchmarks/screens/1m_requests_expressjs.png diff --git a/README.md b/README.md index 84a32096..af3ab603 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ If you're coming from Node.js world, this i -![Iris vs .NET Core(C#)](https://iris-go.com/images/benchmark-iris-vs-netcore.png) +![Iris vs .NET Core(C#) vs Node.js (Express)](https://iris-go.com/images/benchmark-new-gray.png)
Benchmarks from third-party source over the rest web frameworks diff --git a/_benchmarks/README.md b/_benchmarks/README.md index 67598195..b528d3ae 100644 --- a/_benchmarks/README.md +++ b/_benchmarks/README.md @@ -14,11 +14,12 @@ The first test will contain a simple application with a text response and the second will render templates + a layout. -## Simple +## 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. ### .NET Core MVC + ```bash $ cd netcore-mvc $ dotnet run -c Release @@ -43,6 +44,7 @@ Statistics Avg Stdev Max ``` ### Iris MVC + ```bash $ cd iris-mvc $ go run main.go @@ -64,7 +66,6 @@ Statistics Avg Stdev Max Throughput: 19.65MB/s ``` - Click [here](screens) to navigate to the screenshots. ### Summary @@ -104,6 +105,7 @@ Application started. Press Ctrl+C to shut down. ``` ```bash +$ bombardier -c 125 -n 1000000 http://localhost:5000 Bombarding http://localhost:5000 with 1000000 requests using 125 connections 1000000 / 1000000 [=====================================================================================] 100.00% 1m20s Done! @@ -126,6 +128,7 @@ Application started. Press CTRL+C to shut down. ``` ```bash +$ bombardier -c 125 -n 1000000 http://localhost:5000 Bombarding http://localhost:5000 with 1000000 requests using 125 connections 1000000 / 1000000 [======================================================================================] 100.00% 37s Done! @@ -172,6 +175,7 @@ Application started. Press Ctrl+C to shut down. ``` ```bash +$ bombardier -c 125 -n 1000000 http://localhost:5000/api/values/5 Bombarding http://localhost:5000/api/values/5 with 1000000 requests using 125 connections 1000000 / 1000000 [======================================================================================] 100.00% 10s Done! @@ -194,6 +198,7 @@ Application started. Press CTRL+C to shut down. ``` ```bash +$ bombardier -c 125 -n 1000000 http://localhost:5000/api/values/5 Bombarding http://localhost:5000/api/values/5 with 1000000 requests using 125 connections 1000000 / 1000000 [=======================================================================================] 100.00% 8s Done! @@ -206,6 +211,30 @@ Statistics Avg Stdev Max Throughput: 21.93MB/s ``` +### Node.js (Express) + +```bash +$ cd expressjs +$ npm install +$ node app.js +Now listening on: http://localhost:5000 +Application started. Press CTRL+C to shut down. +``` + +```bash +$ bombardier -c 125 -n 1000000 http://localhost:5000/api/values/5 +Bombarding http://localhost:5000/api/values/5 with 1000000 requests using 125 connections + 1000000 / 1000000 [=======================================================================================] 100.00% 1m25s +Done! +Statistics Avg Stdev Max + Reqs/sec 11665.30 628.41 21978 + Latency 10.72ms 1.45ms 112.10ms + HTTP codes: + 1xx - 0, 2xx - 1000000, 3xx - 0, 4xx - 0, 5xx - 0 + others - 0 + Throughput: 3.14MB/s +``` + ### Summary * Time to complete the `1000000 requests` - smaller is better. @@ -218,6 +247,8 @@ Statistics Avg Stdev 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. +Node.js (Express) Application written using **12 code of lines** ran for **1 minute and 25 seconds** serving **11665.30** requests per second with **3.14MB/s** within **10.72ms** latency in average and **112.10ms** max. + ## Sessions Spawn `5000000 requests` with 125 different "threads" targeting a static request path, sets and gets a session based on the name `"key"` and string value `"value"` and write that session value to the response stream. @@ -234,6 +265,7 @@ Application started. Press Ctrl+C to shut down. ``` ```bash +$ bombardier -c 125 -n 5000000 http://localhost:5000/setget Bombarding http://localhost:5000/setget with 5000000 requests using 125 connections 5000000 / 5000000 [====================================================================================] 100.00% 2m40s Done! @@ -256,6 +288,7 @@ Application started. Press CTRL+C to shut down. ``` ```bash +$ bombardier -c 125 -n 5000000 http://localhost:5000/setget Bombarding http://localhost:5000/setget with 5000000 requests using 125 connections 5000000 / 5000000 [====================================================================================] 100.00% 1m15s Done! @@ -268,6 +301,30 @@ Statistics Avg Stdev Max Throughput: 20.65MB/s ``` +### Node.js (Express) with Sessions + +```bash +$ cd expressjs-sessions +$ npm install +$ node app.js +Now listening on: http://localhost:5000 +Application started. Press CTRL+C to shut down. +``` + +```bash +$ bombardier -c 125 -n 5000000 http://localhost:5000/setget +Bombarding http://localhost:5000/setget with 5000000 requests using 125 connections + 5000000 / 5000000 [====================================================================================] 100.00% 15m47s +Done! +Statistics Avg Stdev Max + Reqs/sec 5634.27 2317.30 9945 + Latency 22.17ms 8.19ms 119.08ms + HTTP codes: + 1xx - 0, 2xx - 5000000, 3xx - 0, 4xx - 0, 5xx - 0 + others - 0 + Throughput: 1.48MB/s +``` + ### Summary * Time to complete the `5000000 requests` - smaller is better. @@ -279,11 +336,10 @@ 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! +Node.js (Express) with Sessions Application ran for **15 minutes and 47 seconds** serving **5634.27** requests per second with **1.48MB/s** within **22.17ms** latency in average and **119.08ms** max. > Click [here](screens) to navigate to the screenshots. - ### Articles **Go vs .NET Core in terms of HTTP performance (Sa, 19 August 2017)** @@ -295,5 +351,4 @@ Iris with Sessions Application ran for **1 minute and 15 seconds** serving **667 - https://medium.com/@kataras/iris-go-vs-net-core-kestrel-in-terms-of-http-performance-806195dc93d5 - **Thank you all** for the 100% green feedback, have fun! \ No newline at end of file diff --git a/_benchmarks/expressjs-sessions/app.js b/_benchmarks/expressjs-sessions/app.js new file mode 100644 index 00000000..dc745435 --- /dev/null +++ b/_benchmarks/expressjs-sessions/app.js @@ -0,0 +1,26 @@ +process.env.NODE_ENV = 'production'; + +const express = require('express'); +const app = express(); +const session = require('express-session'); + +// Use the session middleware +app.use(session({ secret: '.cookiesession.id', resave: true, saveUninitialized: false, cookie: { secure: true, maxAge: 60000 } })); + +app.get('/setget', function (req, res) { + req.session.key = 'value'; + + var value = req.session.key; + if (value == '') { + res.send('NOT_OK'); + return; + } + + res.send(value); +}); + +app.listen(5000, function () { + console.log( + 'Now listening on: http://localhost:5000\nApplication started. Press CTRL+C to shut down.' + ) +}); \ No newline at end of file diff --git a/_benchmarks/expressjs-sessions/package-lock.json b/_benchmarks/expressjs-sessions/package-lock.json new file mode 100644 index 00000000..a68c52a3 --- /dev/null +++ b/_benchmarks/expressjs-sessions/package-lock.json @@ -0,0 +1,396 @@ +{ + "name": "expressjs-app", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "accepts": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz", + "integrity": "sha1-hiRnWMfdbSGmR0/whKR0DsBesh8=", + "requires": { + "mime-types": "2.1.17", + "negotiator": "0.6.1" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "body-parser": { + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", + "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", + "requires": { + "bytes": "3.0.0", + "content-type": "1.0.4", + "debug": "2.6.9", + "depd": "1.1.1", + "http-errors": "1.6.2", + "iconv-lite": "0.4.19", + "on-finished": "2.3.0", + "qs": "6.5.1", + "raw-body": "2.3.2", + "type-is": "1.6.15" + } + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" + }, + "content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "crc": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/crc/-/crc-3.4.4.tgz", + "integrity": "sha1-naHpgOO9RPxck79as9ozeNheRms=" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", + "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "encodeurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", + "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "express": { + "version": "4.16.0", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.0.tgz", + "integrity": "sha1-tRljjk61jnF4yBtJjvIveYyy4lU=", + "requires": { + "accepts": "1.3.4", + "array-flatten": "1.1.1", + "body-parser": "1.18.2", + "content-disposition": "0.5.2", + "content-type": "1.0.4", + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "1.1.1", + "encodeurl": "1.0.1", + "escape-html": "1.0.3", + "etag": "1.8.1", + "finalhandler": "1.1.0", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "1.1.2", + "on-finished": "2.3.0", + "parseurl": "1.3.2", + "path-to-regexp": "0.1.7", + "proxy-addr": "2.0.2", + "qs": "6.5.1", + "range-parser": "1.2.0", + "safe-buffer": "5.1.1", + "send": "0.16.0", + "serve-static": "1.13.0", + "setprototypeof": "1.1.0", + "statuses": "1.3.1", + "type-is": "1.6.15", + "utils-merge": "1.0.1", + "vary": "1.1.2" + } + }, + "express-session": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.15.6.tgz", + "integrity": "sha512-r0nrHTCYtAMrFwZ0kBzZEXa1vtPVrw0dKvGSrKP4dahwBQ1BJpF2/y1Pp4sCD/0kvxV4zZeclyvfmw0B4RMJQA==", + "requires": { + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "crc": "3.4.4", + "debug": "2.6.9", + "depd": "1.1.1", + "on-headers": "1.0.1", + "parseurl": "1.3.2", + "uid-safe": "2.1.5", + "utils-merge": "1.0.1" + } + }, + "finalhandler": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz", + "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=", + "requires": { + "debug": "2.6.9", + "encodeurl": "1.0.1", + "escape-html": "1.0.3", + "on-finished": "2.3.0", + "parseurl": "1.3.2", + "statuses": "1.3.1", + "unpipe": "1.0.0" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "http-errors": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", + "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", + "requires": { + "depd": "1.1.1", + "inherits": "2.0.3", + "setprototypeof": "1.0.3", + "statuses": "1.3.1" + }, + "dependencies": { + "setprototypeof": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", + "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" + } + } + }, + "iconv-lite": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", + "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ipaddr.js": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.5.2.tgz", + "integrity": "sha1-1LUFvemUaYfM8PxY2QEP+WB+P6A=" + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" + }, + "mime-db": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", + "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=" + }, + "mime-types": { + "version": "2.1.17", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", + "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", + "requires": { + "mime-db": "1.30.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "negotiator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz", + "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=" + }, + "parseurl": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "proxy-addr": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.2.tgz", + "integrity": "sha1-ZXFQT0e7mI7IGAJT+F3X4UlSvew=", + "requires": { + "forwarded": "0.1.2", + "ipaddr.js": "1.5.2" + } + }, + "qs": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", + "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" + }, + "random-bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", + "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=" + }, + "range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" + }, + "raw-body": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", + "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.2", + "iconv-lite": "0.4.19", + "unpipe": "1.0.0" + } + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + }, + "send": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.0.tgz", + "integrity": "sha1-FjONu5ou3krVe0hCDsO4LY6ApXs=", + "requires": { + "debug": "2.6.9", + "depd": "1.1.1", + "destroy": "1.0.4", + "encodeurl": "1.0.1", + "escape-html": "1.0.3", + "etag": "1.8.1", + "fresh": "0.5.2", + "http-errors": "1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "2.3.0", + "range-parser": "1.2.0", + "statuses": "1.3.1" + } + }, + "serve-static": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.0.tgz", + "integrity": "sha1-gQyR24AOlLoofq5rTgbKq5/cFvE=", + "requires": { + "encodeurl": "1.0.1", + "escape-html": "1.0.3", + "parseurl": "1.3.2", + "send": "0.16.0" + } + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "statuses": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", + "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=" + }, + "type-is": { + "version": "1.6.15", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", + "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=", + "requires": { + "media-typer": "0.3.0", + "mime-types": "2.1.17" + } + }, + "uid-safe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", + "requires": { + "random-bytes": "1.0.0" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + } + } +} diff --git a/_benchmarks/expressjs-sessions/package.json b/_benchmarks/expressjs-sessions/package.json new file mode 100644 index 00000000..39c15b0a --- /dev/null +++ b/_benchmarks/expressjs-sessions/package.json @@ -0,0 +1,15 @@ +{ + "name": "expressjs-app", + "version": "1.0.0", + "description": "", + "main": "app.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Gerasimos (Makis) Maropoulos ", + "license": "ISC", + "dependencies": { + "express": "^4.16.0", + "express-session": "^1.15.6" + } +} \ No newline at end of file diff --git a/_benchmarks/expressjs/app.js b/_benchmarks/expressjs/app.js new file mode 100644 index 00000000..bb6e3d63 --- /dev/null +++ b/_benchmarks/expressjs/app.js @@ -0,0 +1,14 @@ +process.env.NODE_ENV = 'production'; + +const express = require('express'); +const app = express(); + +app.get('/api/values/:id', function (req, res) { + res.send('value'); +}); + +app.listen(5000, function () { + console.log( + 'Now listening on: http://localhost:5000\nApplication started. Press CTRL+C to shut down.' + ) +}); \ No newline at end of file diff --git a/_benchmarks/expressjs/package-lock.json b/_benchmarks/expressjs/package-lock.json new file mode 100644 index 00000000..24d6e0ec --- /dev/null +++ b/_benchmarks/expressjs/package-lock.json @@ -0,0 +1,357 @@ +{ + "name": "expressjs-app", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "accepts": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz", + "integrity": "sha1-hiRnWMfdbSGmR0/whKR0DsBesh8=", + "requires": { + "mime-types": "2.1.17", + "negotiator": "0.6.1" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "body-parser": { + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", + "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", + "requires": { + "bytes": "3.0.0", + "content-type": "1.0.4", + "debug": "2.6.9", + "depd": "1.1.1", + "http-errors": "1.6.2", + "iconv-lite": "0.4.19", + "on-finished": "2.3.0", + "qs": "6.5.1", + "raw-body": "2.3.2", + "type-is": "1.6.15" + } + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" + }, + "content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", + "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "encodeurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", + "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "express": { + "version": "4.16.0", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.0.tgz", + "integrity": "sha1-tRljjk61jnF4yBtJjvIveYyy4lU=", + "requires": { + "accepts": "1.3.4", + "array-flatten": "1.1.1", + "body-parser": "1.18.2", + "content-disposition": "0.5.2", + "content-type": "1.0.4", + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "1.1.1", + "encodeurl": "1.0.1", + "escape-html": "1.0.3", + "etag": "1.8.1", + "finalhandler": "1.1.0", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "1.1.2", + "on-finished": "2.3.0", + "parseurl": "1.3.2", + "path-to-regexp": "0.1.7", + "proxy-addr": "2.0.2", + "qs": "6.5.1", + "range-parser": "1.2.0", + "safe-buffer": "5.1.1", + "send": "0.16.0", + "serve-static": "1.13.0", + "setprototypeof": "1.1.0", + "statuses": "1.3.1", + "type-is": "1.6.15", + "utils-merge": "1.0.1", + "vary": "1.1.2" + } + }, + "finalhandler": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz", + "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=", + "requires": { + "debug": "2.6.9", + "encodeurl": "1.0.1", + "escape-html": "1.0.3", + "on-finished": "2.3.0", + "parseurl": "1.3.2", + "statuses": "1.3.1", + "unpipe": "1.0.0" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "http-errors": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", + "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", + "requires": { + "depd": "1.1.1", + "inherits": "2.0.3", + "setprototypeof": "1.0.3", + "statuses": "1.3.1" + }, + "dependencies": { + "setprototypeof": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", + "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" + } + } + }, + "iconv-lite": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", + "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ipaddr.js": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.5.2.tgz", + "integrity": "sha1-1LUFvemUaYfM8PxY2QEP+WB+P6A=" + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" + }, + "mime-db": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", + "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=" + }, + "mime-types": { + "version": "2.1.17", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", + "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", + "requires": { + "mime-db": "1.30.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "negotiator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "parseurl": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "proxy-addr": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.2.tgz", + "integrity": "sha1-ZXFQT0e7mI7IGAJT+F3X4UlSvew=", + "requires": { + "forwarded": "0.1.2", + "ipaddr.js": "1.5.2" + } + }, + "qs": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", + "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" + }, + "range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" + }, + "raw-body": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", + "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.2", + "iconv-lite": "0.4.19", + "unpipe": "1.0.0" + } + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + }, + "send": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.0.tgz", + "integrity": "sha1-FjONu5ou3krVe0hCDsO4LY6ApXs=", + "requires": { + "debug": "2.6.9", + "depd": "1.1.1", + "destroy": "1.0.4", + "encodeurl": "1.0.1", + "escape-html": "1.0.3", + "etag": "1.8.1", + "fresh": "0.5.2", + "http-errors": "1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "2.3.0", + "range-parser": "1.2.0", + "statuses": "1.3.1" + } + }, + "serve-static": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.0.tgz", + "integrity": "sha1-gQyR24AOlLoofq5rTgbKq5/cFvE=", + "requires": { + "encodeurl": "1.0.1", + "escape-html": "1.0.3", + "parseurl": "1.3.2", + "send": "0.16.0" + } + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "statuses": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", + "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=" + }, + "type-is": { + "version": "1.6.15", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", + "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=", + "requires": { + "media-typer": "0.3.0", + "mime-types": "2.1.17" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + } + } +} diff --git a/_benchmarks/expressjs/package.json b/_benchmarks/expressjs/package.json new file mode 100644 index 00000000..4916ae06 --- /dev/null +++ b/_benchmarks/expressjs/package.json @@ -0,0 +1,14 @@ +{ + "name": "expressjs-app", + "version": "1.0.0", + "description": "", + "main": "app.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Gerasimos (Makis) Maropoulos ", + "license": "ISC", + "dependencies": { + "express": "^4.16.0" + } +} diff --git a/_benchmarks/screens/1m_requests_expressjs.png b/_benchmarks/screens/1m_requests_expressjs.png new file mode 100644 index 0000000000000000000000000000000000000000..7d0603e04193f7adb7e24b6457e4c81e487710e8 GIT binary patch literal 11581 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 D1w@jJ literal 0 HcmV?d00001