[
  {
    "path": ".gitignore",
    "content": "# Vagrant\n.vagrant\n\n# Elixir\n/_build\n/cover\n/deps\nerl_crash.dump\n/servers/**/Gemfile.lock\n*.ez\n*.class\n*.beam\n\n# Python\n/servers/__pycache__\n/servers/*.pyc\n/servers/.wsgi\n\n# Crystal\n*.dwarf\n\n# Executable\ncrystal_server\nfasthttp_server\nhttpbeast_server\n*.aot*\n"
  },
  {
    "path": ".ruby-version",
    "content": "3.0.0\n"
  },
  {
    "path": "README.md",
    "content": "# Table of Contents\n\n* [Scope](#scope)\n  * [Hello World](#hello-world)\n  * [Disclaimer](#disclaimer)\n* [Languages](#languages)\n  * [Ruby](#ruby)\n  * [Python](#python)\n  * [JavaScript](#javascript)\n  * [Dart](#dart)\n  * [Elixir](#elixir)\n  * [Crystal](#crystal)\n  * [Nim](#nim)\n  * [GO](#go)\n* [Tools](#tools)\n  * [Wrk](#wrk)\n  * [Platform](#platform)\n  * [RAM and CPU](#ram-and-cpu)\n* [Benchmarks](#benchmarks)\n  * [Results](#results)\n  * [Puma](#puma)\n  * [Gunicorn with Meinheld](#gunicorn-with-meinheld)\n  * [Node Cluster](#node-cluster)\n  * [Dart HttpServer](#dart-httpserver)\n  * [Plug with Cowboy](#plug-with-cowboy)\n  * [Crystal HTTP](#crystal-http)\n  * [httpbeast](#httpbeast)\n  * [GO ServeMux](#go-servermux)\n  * [Hyper](#hyper)\n\n## Scope\nThe idea behind this repository is to benchmark different languages implementation of HTTP server.\n\n### Hello World\nThe *application* i tested is minimal: the HTTP version of the *Hello World* example.  \nThis approach allows including languages i barely know, since it is pretty easy to find such implementation online.  \nIf you're looking for more complex examples, you will have better luck with the [TechEmpower benchmarks](https://www.techempower.com/benchmarks/).\n\n### Disclaimer\nPlease do take the following numbers with a grain of salt: it is not my intention to promote one language over another basing on micro-benchmarks.  \nIndeed you should never pick a language just basing on its presumed performance.\n\n## Languages\nI have became lazy with years and just adopt languages i can install via `homebrew`, sorry Oracle/MS. This also allows me to benchmark them in a single session, thus trying to use an environment as neutral as possible.\nWhere possible i just relied on the standard library, but when it is not production-ready (i.e. Ruby, Python).\n\n### Ruby\n[Ruby](https://www.ruby-lang.org/en/) 3.0.0 is used. \nRuby is a general-purpose, interpreted, dynamic programming language, focused on simplicity and productivity. \n\n### Python\n[Python](https://www.python.org/) 3.9.1 is used.\nPython is a widely used high-level, general-purpose, interpreted, dynamic programming language.  \n\n### JavaScript\n[Node.js](https://nodejs.org/en/) version 15.5.0 is used.\nNode.js is based on the V8 JavaScript engine, optimized by Google and supporting most of the new language's features.   \n\n### Dart\n[Dart](https://www.dartlang.org/) version 2.10.4 is used.\nDart is a VM based, object-oriented, sound typed language using a C-style syntax that transcompiles optionally into JavaScript.\n\n### Elixir\n[Elixir](http://elixir-lang.org/) 1.11.2 is used.\nElixir is a purely functional language that runs on the [Erlang](https://www.erlang.org/) VM and is strongly influenced by the Ruby syntax.\n\n### Crystal\n[Crystal](http://crystal-lang.org/) 0.35.1 is used.\nCrystal has a syntax very close to Ruby, but brings some desirable features such as statically typing and ahead of time (AOT) compilation.  \n\n### Nim\n[Nim](http://nim-lang.org/) 1.4.2 is used.\nNim is an AOT, Python inspired, statically typed language that comes with an ambitious compiler aimed to produce code in C, C++, JavaScript or ObjectiveC.\n\n### GO\n[GO](https://golang.org/) 1.15.6 is used.\nGO is an AOT language that focuses on simplicity and offers a broad standard library with [CSP](https://en.wikipedia.org/wiki/Communicating_sequential_processes) constructs built in.\n\n## Tools\n\n### Wrk\nI used [wrk](https://github.com/wg/wrk) as the loading tool.  \nI measured each application server six times, picking the best lap (but for VM based languages demanding longer warm-up).  \n```shell\nwrk -t 4 -c 100 -d30s --timeout 2000 http://0.0.0.0:9292\n```\n\n### Platform\nThese benchmarks are recorded on a MacBook PRO 13 2019 having these specs:\n* macOS Catalina\n* 1.4 GHz Quad-Core Intel Core i5\n* 8 GB 2133 MHz LPDDR3\n\n### RAM and CPU\nI measured RAM and CPU consumption by using macOS Activity Monitor dashboard and recording max consumption peak.  \nFor the languages relying on pre-forking parallelism i reported the average consumption by taking a snapshot during the stress period.\n\n## Benchmarks\n\n### Results\n| Language                  | App Server                                        | Requests/sec      | RAM (MB)  | CPU (%)  |\n| :------------------------ | :------------------------------------------------ | ----------------: |---------: |--------: |\n| [Ruby+MJIT](#ruby)        | [Puma](#puma)                                     |         36455.88  |    > 100  |   > 580  |\n| [Elixir](#elixir)         | [Plug with Cowboy](#plug-with-cowboy)             |         46416.25  |     50.5  |   583.8  |\n| [Ruby](#ruby)             | [Puma](#puma)                                     |         47975.36  |    > 100  |   > 580  |\n| [Dart](#dart)             | [Dart HttpServer](#dart-httpserver)               |         59335.33  |    193.2  |   429.1  |\n| [JavaScript](#javascript) | [Node Cluster](#node-cluster)                     |         87208.47  |    > 200  |   > 240  |\n| [GO](#go)                 | [GO ServeMux](#go-servemux)                       |        103847.10  |     10.0  |   429.1  |\n| [Python](#python)         | [Gunicorn with Meinheld](#gunicorn-with-meinheld) |        120105.65  |     > 40  |   > 380  |\n| [Nim](#nim)               | [httpbeast](#httpbeast)                           |        128257.98  |     11.4  |    99.6  |\n| [Crystal](#crystal)       | [Crystal HTTP](#crystal-http)                     |        132699.78  |      8.5  |   246.7  |\n\n                                                                                                   \n### Puma\nI tested Ruby by using a plain [Rack](http://rack.github.io/) application served by [Puma](http://puma.io).  \n\n#### Bootstrap\n```shell\nRUBYOPT='--jit' puma -w 8 -t 2 --preload servers/rack_server.ru\n```\n\n\n### Gunicorn with Meinheld\nI tested Python by using [Gunicorn](http://gunicorn.org/) spawning [Meinheld](http://meinheld.org/) workers with a plain WSGI compliant server.\n\n#### Bootstrap\n```shell\ncd servers\ngunicorn -w 4 -k meinheld.gmeinheld.MeinheldWorker -b :9292 wsgi_server:app\n```\n\n\n### Node Cluster\nI used the cluster module included into Node's standard library.\n\n#### Bootstrap\n```shell\nnode servers/node_server.js\n```\n\n\n### Dart HttpServer\nI used the async HTTP server embedded into the Dart standard library and compiled it with `dart2native` AOT compiler.\n\n#### Bootstrap\n```shell\ndart2native servers/dart_server.dart -k aot\ndartaotruntime servers/dart_server.aot\n```\n\n\n### Plug with Cowboy\nI tested Elixir by using [Plug](https://github.com/elixir-lang/plug) library that provides a [Cowboy](https://github.com/ninenines/cowboy) adapter.\n\n#### Bootstrap\n```shell\ncd servers/plug_server\nMIX_ENV=prod mix compile\nMIX_ENV=prod mix run --no-halt\n```\n\n\n### Crystal HTTP\nI used Crystal HTTP server standard library, enabling parallelism by using the `preview_mt` flag.  \n\n#### Bootstrap\n```shell\ncrystal build -Dpreview_mt --release servers/crystal_server.cr\n./crystal_server\n```\n\n\n### httpbeast\nTo test Nim i opted for the [httpbeast](https://github.com/dom96/httpbeast) library: an asynchronous server relying on Nim HTTP standard library.\n\n#### Bootstrap\n```shell\nnim c -d:release --threads:on servers/httpbeast_server.nim\n./servers/httpbeast_server\n```\n\n\n### GO ServeMux\nI used the [HTTP ServeMux](https://golang.org/pkg/net/http/) GO standard library.\n\n#### Bootstrap\n```shell\ngo run servers/servemux_server.go\n```\n"
  },
  {
    "path": "servers/.gitignore",
    "content": "__pycache__\nnimcache\ngo_server\ncrystal_server\nnim_server\npony_server\n"
  },
  {
    "path": "servers/crystal_server.cr",
    "content": "require \"http/server\"\n\nserver = HTTP::Server.new do |context|\n  context.response.content_type = \"text/plain\"\n  context.response.print \"Hello World\"\nend\n\nserver.listen(9292)\n"
  },
  {
    "path": "servers/dart_server.dart",
    "content": "import 'dart:async';\nimport 'dart:io';\nimport 'dart:isolate';\n\nconst String _HOST = '0.0.0.0';\nconst String _GREET = 'Hello World';\n\n_startServer(arg) async {\n  var server = await HttpServer.bind(_HOST, 9292, shared: true);\n  await for (HttpRequest request in server) {\n    request.response\n      ..write(_GREET)\n      ..close();\n  }\n}\n\nvoid main() {\n  final cpus = Platform.numberOfProcessors;\n  for (int i = 0; i < cpus; i++)\n    Isolate.spawn(_startServer, null);\n  _startServer(null);\n}\n"
  },
  {
    "path": "servers/httpbeast_server.nim",
    "content": "import asyncdispatch, httpbeast, options\n\nconst settings = httpbeast.initSettings(Port(9292))\n\nproc onRequest(req: Request): Future[void] =\n  if req.httpMethod == some(HttpGet):\n      const data = \"Hello World\"\n      const headers = \"Content-Type: text/plain\"\n      req.send(Http200, data, headers)\n\nrun(onRequest, settings)\n"
  },
  {
    "path": "servers/node_server.js",
    "content": "const cluster = require('cluster');\nconst http = require('http');\nconst numCPUs = require('os').cpus().length;\n\nif (cluster.isMaster) {\n  for (let i = 0; i < numCPUs; i++) {\n    cluster.fork();\n  }\n} else {\n  http.createServer((req, res) => {\n    res.writeHead(200);\n    res.end('Hello World');\n  }).listen(9292);\n}\n"
  },
  {
    "path": "servers/plug_server/.gitignore",
    "content": "/_build\n/cover\n/deps\nerl_crash.dump\n*.ez\n./*.beam\nmix.lock\n"
  },
  {
    "path": "servers/plug_server/lib/app.ex",
    "content": "defmodule App do\n  use Application\n\n  def start(_type, _args) do\n    children = [\n      Plug.Cowboy.child_spec(scheme: :http, plug: Server, options: [port: 9292])\n    ]\n\n    opts = [strategy: :one_for_one, name: App.Supervisor]\n    Supervisor.start_link(children, opts)\n  end\nend\n"
  },
  {
    "path": "servers/plug_server/lib/server.ex",
    "content": "defmodule Server do\n  import Plug.Conn\n\n  def init(opts), do: opts\n\n  def call(conn, _opts) do\n    conn\n    |> put_resp_content_type(\"text/plain\")\n    |> send_resp(200, \"Hello world\")\n  end\nend\n"
  },
  {
    "path": "servers/plug_server/mix.exs",
    "content": "defmodule PlugServer.Mixfile do\n  use Mix.Project\n\n  def project do\n    [app: :server,\n     version: \"1.2.0\",\n     elixir: \"~> 1.8\",\n     start_permanent: Mix.env == :prod,\n     deps: deps()]\n  end\n\n  def application do\n    [applications: [:cowboy, :plug],\n     mod: {App, []},\n     env: [cowboy_port: 9292]]\n  end\n\n  defp deps do\n   [{:cowboy, \"~> 2.6.3\"},\n    {:plug, \"~> 1.8\"},\n    {:plug_cowboy, \"~> 2.0.2\"}]\n  end\nend\n"
  },
  {
    "path": "servers/rack_server.ru",
    "content": "run Proc.new { |env| ['200', {'Content-Type' => 'text/plain'}, ['Hello World']] }\n"
  },
  {
    "path": "servers/servemux_server.go",
    "content": "package main\t\n\n import (\t\n\t\"fmt\"\t\n\t\"log\"\t\n\t\"net/http\"\t\n)\t\n\n func main() {\t\n\thttp.HandleFunc(\"/\", func(w http.ResponseWriter, r *http.Request) {\t\n\t\tfmt.Fprint(w, \"Hello World\")\t\n\t})\t\n\tlog.Fatal(http.ListenAndServe(\"0.0.0.0:9292\", nil))\t\n}\n"
  },
  {
    "path": "servers/wsgi_server.py",
    "content": "BODY = b'Hello World'\nLEN = str(len(BODY))\n\n\ndef app(_, resp):\n    body = BODY\n    resp('200 OK', [('Content-Type', 'text/plain'), ('Content-Length', LEN)])\n    yield body\n"
  }
]