├── README.md ├── docker-compose ├── docker-compose.yml ├── go ├── Dockerfile └── martini.go ├── node └── express │ ├── .gitignore │ ├── Dockerfile │ ├── app.js │ └── package.json ├── python └── flask │ ├── Dockerfile │ ├── app.py │ └── requirements.txt └── ruby ├── .ruby-version ├── Dockerfile ├── Gemfile ├── Gemfile.lock └── sinatra.rb /README.md: -------------------------------------------------------------------------------- 1 | # Fibonacci Web Application Benchmark 2 | 3 | `fibonacci-webapp-benchmark` contains dockerized web-apps written in different languages and frameworks that get a number as url parameter and calculate the corresponding fibonacci number. 4 | 5 | All these implementations are used for benchmarking. 6 | 7 | ## Requirements 8 | 9 | - [docker](https://www.docker.com/) 10 | - [ab](https://httpd.apache.org/docs/2.4/programs/ab.html) 11 | 12 | ## Usage 13 | 14 | Run `./docker-compose up -d` against this repository: 15 | 16 | ``` 17 | $ ./docker-compose up -d 18 | ``` 19 | 20 | Then execute the command: 21 | 22 | ``` 23 | $ docker ps 24 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 25 | 14e0d2388dca fibonacciwebappbenchmark_node "npm start" 6 seconds ago Up 5 seconds 0.0.0.0:8080->8080/tcp fibonacciwebappbenchmark_node_1 26 | 8b1bdd070f83 fibonacciwebappbenchmark_ruby "bundle exec ruby sin" 23 seconds ago Up 22 seconds 0.0.0.0:4567->4567/tcp fibonacciwebappbenchmark_ruby_1 27 | 333360123b56 fibonacciwebappbenchmark_go "go run martini.go" 34 seconds ago Up 32 seconds 0.0.0.0:3000->3000/tcp fibonacciwebappbenchmark_go_1 28 | df50829f511b fibonacciwebappbenchmark_python "python app.py" 42 seconds ago Up 41 seconds 0.0.0.0:5000->5000/tcp fibonacciwebappbenchmark_python_1 29 | ``` 30 | 31 | ## Screenshots 32 | 33 | Now you can open your favorite Web Browser and visit http://localhost:{port}/{number}, for example: 34 | 35 | Open http://localhost:5000/10, and you can see: 36 | 37 | ``` 38 | Python + Flask 39 | 40 | fib(10): 55 41 | ``` 42 | 43 | ## Benchmark 44 | 45 | ``` 46 | $ ab -n 100000 -c 100 http://localhost:5000/10 47 | This is ApacheBench, Version 2.3 <$Revision: 1430300 $> 48 | Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ 49 | Licensed to The Apache Software Foundation, http://www.apache.org/ 50 | 51 | Benchmarking localhost (be patient) 52 | Completed 10000 requests 53 | Completed 20000 requests 54 | Completed 30000 requests 55 | Completed 40000 requests 56 | Completed 50000 requests 57 | Completed 60000 requests 58 | Completed 70000 requests 59 | Completed 80000 requests 60 | Completed 90000 requests 61 | Completed 100000 requests 62 | Finished 100000 requests 63 | 64 | 65 | Server Software: Werkzeug/0.11.4 66 | Server Hostname: localhost 67 | Server Port: 5000 68 | 69 | Document Path: /10 70 | Document Length: 29 bytes 71 | 72 | Concurrency Level: 100 73 | Time taken for tests: 171.011 seconds 74 | Complete requests: 100000 75 | Failed requests: 0 76 | Write errors: 0 77 | Total transferred: 18400000 bytes 78 | HTML transferred: 2900000 bytes 79 | Requests per second: 584.76 [#/sec] (mean) 80 | Time per request: 171.011 [ms] (mean) 81 | Time per request: 1.710 [ms] (mean, across all concurrent requests) 82 | Transfer rate: 105.07 [Kbytes/sec] received 83 | 84 | Connection Times (ms) 85 | min mean[+/-sd] median max 86 | Connect: 0 0 0.3 0 10 87 | Processing: 21 171 16.2 168 284 88 | Waiting: 6 170 15.9 167 276 89 | Total: 21 171 16.2 169 284 90 | 91 | Percentage of the requests served within a certain time (ms) 92 | 50% 169 93 | 66% 174 94 | 75% 178 95 | 80% 180 96 | 90% 189 97 | 95% 200 98 | 98% 218 99 | 99% 233 100 | 100% 284 (longest request) 101 | ``` 102 | 103 | For more detailed results, please refer to my blog: 104 | 105 | http://startover.github.io/articles/2016/03/15/python-ruby-go-node-benchmark/ 106 | 107 | 108 | ## Links 109 | 110 | Related articles: 111 | 112 | - https://medium.com/@tschundeee/express-vs-flask-vs-go-acc0879c2122#.wfmtvnnxq 113 | - https://realpython.com/blog/python/python-ruby-and-golang-a-web-Service-application-comparison/ 114 | 115 | -------------------------------------------------------------------------------- /docker-compose: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/startover/fibonacci-webapp-benchmark/681498fa9c3eab80d059f66b2fddbfebd15ea788/docker-compose -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | python: 2 | build: python/flask 3 | command: python app.py 4 | ports: 5 | - "5000:5000" 6 | ruby: 7 | build: ruby 8 | command: bundle exec ruby sinatra.rb -o '0.0.0.0' 9 | ports: 10 | - "4567:4567" 11 | go: 12 | build: go 13 | command: go run martini.go 14 | ports: 15 | - "3000:3000" 16 | node: 17 | build: node/express 18 | command: npm start 19 | ports: 20 | - "8080:8080" 21 | -------------------------------------------------------------------------------- /go/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.4-alpine 2 | 3 | RUN apk add --update --no-cache git 4 | 5 | ADD . /go/src/github.com/startover/go-martini-fibonacci 6 | WORKDIR /go/src/github.com/startover/go-martini-fibonacci 7 | 8 | RUN go get github.com/go-martini/martini 9 | -------------------------------------------------------------------------------- /go/martini.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "github.com/go-martini/martini" 7 | ) 8 | 9 | func fib(n int) int { 10 | if n < 2 { 11 | return n 12 | } 13 | return fib(n-2) + fib(n-1) 14 | } 15 | 16 | func main() { 17 | app := martini.Classic() 18 | app.Get("/(?P[0-9]+)", func(params martini.Params) string { 19 | number, _ := strconv.Atoi(params["number"]) 20 | return fmt.Sprintf("Go + Martini
fib(%s): %s", params["number"], strconv.Itoa(fib(number))) 21 | }) 22 | app.Run() 23 | } 24 | -------------------------------------------------------------------------------- /node/express/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /node/express/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mhart/alpine-node:4 2 | 3 | ADD . /app 4 | WORKDIR /app 5 | 6 | RUN npm install 7 | -------------------------------------------------------------------------------- /node/express/app.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var app = express(); 3 | 4 | var fib = function (n) { 5 | if (n === 0) { 6 | return 0; 7 | } else if (n == 1) { 8 | return 1; 9 | } else { 10 | return fib(n - 1) + fib(n - 2) 11 | } 12 | }; 13 | 14 | app.get("/:number", function (req, res) { 15 | var number = req.param('number'); 16 | var result = fib(number); 17 | res.send("Node + Express
fib("+number+"): "+result); 18 | }); 19 | 20 | app.listen(8080); 21 | -------------------------------------------------------------------------------- /node/express/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-express-fibonacci", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "start": "node app.js" 7 | }, 8 | "dependencies": { 9 | "express": "~4.13.1", 10 | "jade": "~1.11.0", 11 | "serve-favicon": "~2.3.0" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /python/flask/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:2.7-alpine 2 | 3 | ADD . /app 4 | WORKDIR /app 5 | 6 | RUN pip install -r requirements.txt 7 | -------------------------------------------------------------------------------- /python/flask/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | app = Flask(__name__) 3 | 4 | 5 | @app.route('/') 6 | def index(number=1): 7 | return "Python + Flask
fib("+ str(number) + "): " + str(fib(number)) 8 | 9 | 10 | def fib(n): 11 | if n == 0: 12 | return 0 13 | elif n == 1: 14 | return 1 15 | else: 16 | return fib(n - 1) + fib(n - 2) 17 | 18 | if __name__ == '__main__': 19 | app.run(host="0.0.0.0", debug=False) 20 | -------------------------------------------------------------------------------- /python/flask/requirements.txt: -------------------------------------------------------------------------------- 1 | flask==0.10.1 2 | -------------------------------------------------------------------------------- /ruby/.ruby-version: -------------------------------------------------------------------------------- 1 | 2.2.0 2 | -------------------------------------------------------------------------------- /ruby/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ruby:2.2-alpine 2 | 3 | ADD . /app 4 | WORKDIR /app 5 | 6 | RUN bundle install 7 | -------------------------------------------------------------------------------- /ruby/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://ruby.taobao.org' 2 | 3 | gem 'sinatra' 4 | -------------------------------------------------------------------------------- /ruby/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://ruby.taobao.org/ 3 | specs: 4 | rack (1.6.4) 5 | rack-protection (1.5.3) 6 | rack 7 | sinatra (1.4.7) 8 | rack (~> 1.5) 9 | rack-protection (~> 1.4) 10 | tilt (>= 1.3, < 3) 11 | tilt (2.0.2) 12 | 13 | PLATFORMS 14 | ruby 15 | 16 | DEPENDENCIES 17 | sinatra 18 | 19 | BUNDLED WITH 20 | 1.11.2 21 | -------------------------------------------------------------------------------- /ruby/sinatra.rb: -------------------------------------------------------------------------------- 1 | require 'sinatra' 2 | 3 | def fib(n) 4 | if n == 0 5 | return 0 6 | elsif n == 1 7 | return 1 8 | else 9 | return fib(n-1) + fib(n-2) 10 | end 11 | end 12 | 13 | get '/:number' do 14 | "Ruby + Sinatra
fib(#{params[:number]}): #{fib(params[:number].to_i).to_s}" 15 | end 16 | --------------------------------------------------------------------------------