├── README.md ├── erlang └── cowboy │ ├── Makefile │ ├── README.md │ ├── ebin │ └── fib.app │ ├── rebar │ ├── rebar.config │ └── src │ ├── fib.app.src │ ├── fib_app.erl │ ├── fib_index_handler.erl │ └── fib_sup.erl ├── go ├── gorilla.go ├── httprouter.go ├── pat.go └── vanilla.go ├── java └── spark.java ├── node └── express │ ├── .gitignore │ ├── app.js │ ├── appcluster.js │ └── package.json ├── php └── index.php ├── python ├── flasksapp.py ├── tornadosrv.py └── twistedapp.py ├── ruby ├── .gitignore ├── Gemfile ├── README.md └── sinatra.rb └── scala └── playframework ├── .gitignore ├── README.md ├── app └── controllers │ └── Application.scala ├── build.sbt ├── conf ├── application.conf └── routes └── project ├── build.properties └── plugins.sbt /README.md: -------------------------------------------------------------------------------- 1 | # Fibonacci Web Framework/Platform Shootout 2 | 3 | This repository contains web-apps written in different languages and frameworks that get a number as url parameter and calculate the corresponding fibonacci number. 4 | 5 | These implementations are used for benchmarking. The original story can be found here: 6 | 7 | https://medium.com/@tschundeee/express-vs-flask-vs-go-acc0879c2122 8 | 9 | ## The Task 10 | I decided to do a little benchmark of some of my favorite web stacks. So I created a web service that calculates the corresponding fibonacci number to a request parameter. 11 | 12 | ## The Code 13 | A request to for example http://localhost:3000/10 calculates fib(10) and returns the result (55 in this case) as plain text. I think it’s better to have a little computation per each request because it is more realistic than just serving “Hello World”. 14 | 15 | -------------------------------------------------------------------------------- /erlang/cowboy/Makefile: -------------------------------------------------------------------------------- 1 | REBAR = ./rebar 2 | 3 | .PHONY: all deps compile clean 4 | 5 | all: deps compile 6 | 7 | run: 8 | erl -pa ./deps/cowboy/ebin ./deps/cowlib/ebin ./deps/ranch/ebin ./ebin -s fib_app 9 | 10 | deps: 11 | $(REBAR) get-deps 12 | 13 | compile: 14 | $(REBAR) compile 15 | 16 | clean: 17 | $(REBAR) clean 18 | rm -f erl_crash.dump 19 | -------------------------------------------------------------------------------- /erlang/cowboy/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Erlang Cowboy Version of the Fibonacci Web Lang Benchmark 3 | 4 | Author: Yüce Tekol 5 | 6 | ## Info 7 | 8 | This sample uses version 1.0.1 of [Cowboy](https://github.com/ninenines/cowboy.git). 9 | 10 | The router is at `src/fib_app.erl`. Cowboy can constrain the arguments passed to the handler (*number* in this case), but it causes the number of requests per second to drop about 1000 on my machine, so I didn't use it. 11 | 12 | The handler is `src/fib_index_handler.erl`. 13 | 14 | The proper way to run an OTP based Erlang application is creating a release first, but that's unnecessary for just trying out this simple code. 15 | 16 | ## Instructions 17 | 18 | Compile with: 19 | 20 | make compile 21 | 22 | and then run with: 23 | 24 | make run 25 | 26 | The app runs at `localhost:8088` by default. 27 | 28 | You can exit the Erlang shell by pressing Ctrl+C twice. 29 | -------------------------------------------------------------------------------- /erlang/cowboy/ebin/fib.app: -------------------------------------------------------------------------------- 1 | {application,fib, 2 | [{description,"Fibonacci Web Lang Benchmark"}, 3 | {vsn,"1"}, 4 | {registered,[]}, 5 | {applications,[kernel,stdlib,cowboy]}, 6 | {mod,{fib_app,[]}}, 7 | {env,[]}, 8 | {modules,[fib_app,fib_index_handler,fib_sup]}]}. 9 | -------------------------------------------------------------------------------- /erlang/cowboy/rebar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gobijan/fibonacci-web-lang-benchmark/027a18bd4e08bcadc457dc6b224318fdb3396d79/erlang/cowboy/rebar -------------------------------------------------------------------------------- /erlang/cowboy/rebar.config: -------------------------------------------------------------------------------- 1 | {deps, [ 2 | {cowboy, ".*", {git, "https://github.com/ninenines/cowboy.git", 3 | {tag, "1.0.1"}}}]}. 4 | -------------------------------------------------------------------------------- /erlang/cowboy/src/fib.app.src: -------------------------------------------------------------------------------- 1 | {application, fib, 2 | [ 3 | {description, "Fibonacci Web Lang Benchmark"}, 4 | {vsn, "1"}, 5 | {registered, []}, 6 | {applications, [ 7 | kernel, 8 | stdlib, 9 | cowboy 10 | ]}, 11 | {mod, { fib_app, []}}, 12 | {env, []} 13 | ]}. 14 | -------------------------------------------------------------------------------- /erlang/cowboy/src/fib_app.erl: -------------------------------------------------------------------------------- 1 | -module(fib_app). 2 | 3 | -behaviour(application). 4 | 5 | -export([start/0]). 6 | -export([start/2, stop/1]). 7 | 8 | start() -> 9 | application:ensure_all_started(fib). 10 | 11 | start(_StartType, _StartArgs) -> 12 | Dispatch = cowboy_router:compile([ 13 | {'_', [ 14 | {"/:number", fib_index_handler, []}]}]), 15 | {ok, _} = cowboy:start_http(http, 100, [{port, 8088}], 16 | [{env, [{dispatch, Dispatch}]}]), 17 | fib_sup:start_link(). 18 | 19 | stop(_State) -> 20 | ok. 21 | -------------------------------------------------------------------------------- /erlang/cowboy/src/fib_index_handler.erl: -------------------------------------------------------------------------------- 1 | -module(fib_index_handler). 2 | -behaviour(cowboy_http_handler). 3 | 4 | -export([init/3, handle/2, terminate/3]). 5 | 6 | init(_Transport, Req, _Opts) -> 7 | {ok, Req, []}. 8 | 9 | handle(Req, State) -> 10 | {BinNumber, Req1} = cowboy_req:binding(number, Req), 11 | Fib = fib(binary_to_integer(BinNumber)), 12 | BinFib = integer_to_binary(Fib), 13 | Content = [<<"Erlang + Cowboy
fib(">>, BinNumber, <<"): ">>, BinFib], 14 | {ok, Resp} = cowboy_req:reply(200, 15 | [{<<"content-type">>, <<"text/html">>}], 16 | Content, 17 | Req1), 18 | {ok, Resp, State}. 19 | 20 | terminate(_Reason, _Req, _State) -> 21 | ok. 22 | 23 | fib(0) -> 0; 24 | fib(1) -> 1; 25 | fib(N) when N > 0 -> 26 | fib(N - 1) + fib(N - 2). 27 | -------------------------------------------------------------------------------- /erlang/cowboy/src/fib_sup.erl: -------------------------------------------------------------------------------- 1 | -module(fib_sup). 2 | 3 | -behaviour(supervisor). 4 | 5 | %% API 6 | -export([start_link/0]). 7 | 8 | %% Supervisor callbacks 9 | -export([init/1]). 10 | 11 | %% Helper macro for declaring children of supervisor 12 | -define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}). 13 | 14 | %% =================================================================== 15 | %% API functions 16 | %% =================================================================== 17 | 18 | start_link() -> 19 | supervisor:start_link({local, ?MODULE}, ?MODULE, []). 20 | 21 | %% =================================================================== 22 | %% Supervisor callbacks 23 | %% =================================================================== 24 | 25 | init([]) -> 26 | {ok, { {one_for_one, 5, 10}, []} }. 27 | 28 | -------------------------------------------------------------------------------- /go/gorilla.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "runtime" 7 | "strconv" 8 | 9 | "github.com/gorilla/mux" 10 | ) 11 | 12 | // fib returns a function that returns 13 | // successive Fibonacci numbers. 14 | func fib(n int) int { 15 | if n == 0 { 16 | return 0 17 | } 18 | if n == 1 { 19 | return 1 20 | } 21 | return fib(n-1) + fib(n-2) 22 | } 23 | 24 | func indexHandler(w http.ResponseWriter, r *http.Request) { 25 | vars := mux.Vars(r) 26 | stringNumber := vars["number"] 27 | number, _ := strconv.Atoi(stringNumber) 28 | fmt.Fprintln(w, fib(number)) 29 | } 30 | 31 | func main() { 32 | runtime.GOMAXPROCS(runtime.NumCPU() - 1) // one core for wrk 33 | r := mux.NewRouter() 34 | r.HandleFunc("/{number:[0-9]+}", indexHandler) 35 | http.Handle("/", r) 36 | http.ListenAndServe(":4000", nil) 37 | } 38 | -------------------------------------------------------------------------------- /go/httprouter.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "runtime" 7 | "strconv" 8 | 9 | "github.com/julienschmidt/httprouter" 10 | ) 11 | 12 | // fib returns a function that returns 13 | // successive Fibonacci numbers. 14 | func fib(n int) int { 15 | if n == 0 { 16 | return 0 17 | } 18 | if n == 1 { 19 | return 1 20 | } 21 | return fib(n-1) + fib(n-2) 22 | } 23 | 24 | func indexHandler(w http.ResponseWriter, r *http.Request, p httprouter.Params) { 25 | stringNumber := p.ByName("number") 26 | number, _ := strconv.Atoi(stringNumber) 27 | fmt.Fprintln(w, fib(number)) 28 | } 29 | 30 | func main() { 31 | runtime.GOMAXPROCS(runtime.NumCPU() - 1) // one core for wrk 32 | r := httprouter.New() 33 | r.GET("/:number", indexHandler) 34 | http.ListenAndServe(":4000", r) 35 | } 36 | -------------------------------------------------------------------------------- /go/pat.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "runtime" 7 | "strconv" 8 | 9 | "github.com/bmizerany/pat" 10 | ) 11 | 12 | // fib returns a function that returns 13 | // successive Fibonacci numbers. 14 | func fib(n int) int { 15 | if n == 0 { 16 | return 0 17 | } 18 | if n == 1 { 19 | return 1 20 | } 21 | return fib(n-1) + fib(n-2) 22 | } 23 | 24 | func indexHandler(w http.ResponseWriter, r *http.Request) { 25 | stringNumber := r.URL.Query().Get(":number") 26 | number, _ := strconv.Atoi(stringNumber) 27 | fmt.Fprintln(w, fib(number)) 28 | } 29 | 30 | func main() { 31 | runtime.GOMAXPROCS(runtime.NumCPU() - 1) // one core for wrk 32 | m := pat.New() 33 | //m.Get("/{number:[0-9]+}", http.HandlerFunc(IndexHandler)) 34 | m.Get("/:number", http.HandlerFunc(indexHandler)) 35 | http.Handle("/", m) 36 | http.ListenAndServe(":4000", nil) 37 | } 38 | -------------------------------------------------------------------------------- /go/vanilla.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "strconv" 7 | "strings" 8 | ) 9 | 10 | func fib(n int) int { 11 | if n == 0 { 12 | return 0 13 | } 14 | if n == 1 { 15 | return 1 16 | } 17 | return fib(n-1) + fib(n-2) 18 | } 19 | 20 | func indexHandler(w http.ResponseWriter, r *http.Request) { 21 | n := strings.Trim(r.URL.Path, "/") 22 | number, err := strconv.Atoi(n) 23 | if err != nil { 24 | http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) 25 | return 26 | } 27 | fmt.Fprintln(w, fib(number)) 28 | } 29 | 30 | func main() { 31 | http.HandleFunc("/", indexHandler) 32 | http.ListenAndServe(":4000", nil) 33 | fmt.Println("Fin Bench running on Port 4000") 34 | } 35 | -------------------------------------------------------------------------------- /java/spark.java: -------------------------------------------------------------------------------- 1 | import static spark.Spark.get; 2 | 3 | public class FibBench { 4 | public static void main(String[] args) { 5 | 6 | get("/:number", (req, res) -> { 7 | int number = Integer.parseInt(req.params(":number")); 8 | return "Sparkjava: fib(" + number +") = " + fib(number); 9 | } 10 | ); 11 | 12 | } 13 | 14 | public static int fib(int n) { 15 | if (n == 0) { 16 | return 0; 17 | } else if (n == 1) { 18 | return 1; 19 | } else { 20 | return fib(n - 1) + fib(n - 2); 21 | } 22 | } 23 | 24 | } -------------------------------------------------------------------------------- /node/express/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /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(3000); -------------------------------------------------------------------------------- /node/express/appcluster.js: -------------------------------------------------------------------------------- 1 | var cluster = require('cluster'); 2 | 3 | if (cluster.isMaster) { 4 | // Code to run if we're in the master process 5 | 6 | var numCPUs = require('os').cpus().length - 1; // leave 1 core for wrk 7 | // Fork workers. 8 | for (var i = 0; i < numCPUs; i++) { 9 | cluster.fork(); 10 | } 11 | } else { 12 | // Code to run if we're in a worker process 13 | var express = require("express"); 14 | var app = express(); 15 | 16 | app.get("/:number", function(req, res) { 17 | var number = req.param('number'); 18 | var result = fib(number); 19 | res.send("Node + Express
fib(" + number + "): " + result); 20 | }); 21 | app.listen(3000); 22 | console.log("Express Worker running.") 23 | } 24 | 25 | 26 | var fib = function(n) { 27 | if (n === 0) { 28 | return 0; 29 | } else if (n == 1) { 30 | return 1; 31 | } else { 32 | return fib(n - 1) + fib(n - 2) 33 | } 34 | }; -------------------------------------------------------------------------------- /node/express/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "application-name", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "start": "node app.js" 7 | }, 8 | "dependencies": { 9 | "express": "3.5.1", 10 | "jade": "*", 11 | "less-middleware": "~0.1.15" 12 | } 13 | } -------------------------------------------------------------------------------- /php/index.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /python/flasksapp.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | app = Flask(__name__) 3 | 4 | @app.route('/') 5 | def index(number=1): 6 | return "Python + Flask
fib("+ str(number) + "): " + str(fib(number)) 7 | 8 | def fib(n): 9 | if n == 0: 10 | return 0 11 | elif n == 1: 12 | return 1 13 | else: 14 | return fib(n - 1) + fib(n - 2) 15 | 16 | if __name__ == '__main__': 17 | app.run(debug=False) 18 | -------------------------------------------------------------------------------- /python/tornadosrv.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from tornado.httpserver import HTTPServer 3 | from tornado.ioloop import IOLoop 4 | import tornado.web 5 | 6 | def fib(n): 7 | if n == 0: 8 | return 0 9 | elif n == 1: 10 | return 1 11 | else: 12 | return fib(n - 1) + fib(n - 2) 13 | 14 | class FibHandler(tornado.web.RequestHandler): 15 | @tornado.web.asynchronous 16 | def get(self, number): 17 | number = int(number) 18 | self.write("Python + Flask
fib("+ str(number) + "): " + str(fib(number))) 19 | self.finish() 20 | 21 | application = tornado.web.Application([ 22 | (r"/(.+)", FibHandler), 23 | ]) 24 | 25 | if __name__ == "__main__": 26 | num_proc = int(sys.argv[1]) if len(sys.argv) > 1 else 1 27 | print("start tornado with {0} processes".format(num_proc)) 28 | server = HTTPServer(application) 29 | server.bind(8888) 30 | server.start(num_proc) 31 | IOLoop.instance().start() 32 | -------------------------------------------------------------------------------- /python/twistedapp.py: -------------------------------------------------------------------------------- 1 | import re 2 | from twisted.web.server import Site 3 | from twisted.web.resource import Resource 4 | from twisted.internet import reactor 5 | 6 | 7 | def fib(n): 8 | if n == 0: 9 | return 0 10 | elif n == 1: 11 | return 1 12 | else: 13 | return fib(n-1) + fib(n-2) 14 | 15 | 16 | class FibonacciResource(Resource): 17 | isLeaf = True 18 | def __init__(self, num): 19 | self.num = int(num) 20 | 21 | def render_GET(self, request): 22 | return "Python + Twisted
fib(%s): %s" % (self.num, fib(self.num)) 23 | 24 | 25 | class Router(Resource): 26 | def getChild(self, path, request): 27 | if re.match('\d+', path): 28 | return FibonacciResource(path) 29 | return self 30 | 31 | 32 | reactor.listenTCP(5000, Site(Router())) 33 | reactor.run() -------------------------------------------------------------------------------- /ruby/.gitignore: -------------------------------------------------------------------------------- 1 | Gemfile.lock -------------------------------------------------------------------------------- /ruby/Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # A sample Gemfile 3 | source 'https://rubygems.org' 4 | 5 | # gem "rails" 6 | gem 'sinatra' 7 | gem 'thin' 8 | -------------------------------------------------------------------------------- /ruby/README.md: -------------------------------------------------------------------------------- 1 | # Ruby Fibonacci Example 2 | Run the following commands: 3 | 4 | 1. bundle install 5 | 2. ruby sinatra.rb 6 | 7 | Benchmark it. -------------------------------------------------------------------------------- /ruby/sinatra.rb: -------------------------------------------------------------------------------- 1 | require 'sinatra' 2 | set :logging, false 3 | 4 | def fib(n) 5 | if n.zero? 6 | return 0 7 | elsif n == 1 8 | return 1 9 | else 10 | return fib(n - 1) + fib(n - 2) 11 | end 12 | end 13 | 14 | get '/:number' do 15 | fib(params[:number].to_i).to_s 16 | end 17 | -------------------------------------------------------------------------------- /scala/playframework/.gitignore: -------------------------------------------------------------------------------- 1 | logs 2 | project/project 3 | project/target 4 | target 5 | .target 6 | tmp 7 | .history 8 | .cache 9 | dist 10 | bin 11 | /.idea 12 | /*.iml 13 | /out 14 | /.idea_modules 15 | /.classpath 16 | /.project 17 | /RUNNING_PID 18 | /.settings 19 | -------------------------------------------------------------------------------- /scala/playframework/README.md: -------------------------------------------------------------------------------- 1 | # Fibonacci web lang benchmark using Scala + Play Framework 2 | 3 | ## Set up & Run 4 | * Set up [sbt](http://www.scala-sbt.org/release/tutorial/Setup.html) 5 | * Run following commands in this directory. 6 | * Run `sbt dist` *First time, it will download lot of dependency jars* 7 | * cd `target/universal` 8 | * unzip `fibonacciworld-1.0.0-SNAPSHOT.zip` 9 | * Run ./fibonacciworld-1.0.0-SNAPSHOT/bin/fibonacciworld 10 | -------------------------------------------------------------------------------- /scala/playframework/app/controllers/Application.scala: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import play.api.mvc._ 4 | import play.api.libs.concurrent.Execution.Implicits.defaultContext 5 | 6 | object Application extends Controller { 7 | 8 | def findFib(num:Int) = Action.async { 9 | val futureInt = scala.concurrent.Future { fib(num) } 10 | futureInt.map(i => Ok(i.toString)) 11 | } 12 | 13 | def fib(n:Int):Int = { 14 | n match { 15 | case 0 => 0 16 | case 1 => 1 17 | case _ => fib(n-1) + fib(n-2) 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /scala/playframework/build.sbt: -------------------------------------------------------------------------------- 1 | name := "FibonacciWorld" 2 | 3 | version := "1.0.0-SNAPSHOT" 4 | 5 | lazy val root = (project in file(".")).enablePlugins(PlayScala) -------------------------------------------------------------------------------- /scala/playframework/conf/application.conf: -------------------------------------------------------------------------------- 1 | application.secret="[N`QovJ[3fSLHSsnDYGqad7V]?u342=;g4[^STf_jmEmGKX0=4PpK0jrh2/v7dRP" 2 | application.langs="en" -------------------------------------------------------------------------------- /scala/playframework/conf/routes: -------------------------------------------------------------------------------- 1 | #Call fibonacci 2 | GET /:n controllers.Application.findFib(n:Int) 3 | -------------------------------------------------------------------------------- /scala/playframework/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.8 -------------------------------------------------------------------------------- /scala/playframework/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | // The Typesafe repository 2 | resolvers += "Typesafe repository" at "https://repo.typesafe.com/typesafe/releases/" 3 | 4 | // Use the Play sbt plugin for Play projects 5 | addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.4.1") --------------------------------------------------------------------------------